Cursor rule
.cursor/rules/chat-window-accessibility.mdcChat window component accessibility compliance and WCAG compliance for real-time communication features
Cursor rules
Quality
24/100
Scores the file, not the repository.Length
3,916 words
1 headings · 12 code blocksRepository
428
— · pushed 17 days agoLast changed
3 days ago
First indexed 3 days ago.123456# Chat Window Component Accessibility Standards78Ensures chat window components follow WCAG compliance and provide proper accessibility for real-time communication, notifications, and assistive technology support.910<rule>11name: chat_window_accessibility_standards12description: Enforce chat window component accessibility standards and WCAG compliance for real-time communication features13filters:14 - type: file_extension15 pattern: "\\.(vue|jsx|tsx|html|liquid|php|js|ts|css|scss|sass|less)$"1617actions:18 - type: enforce19 conditions:20 # Chat window missing proper landmark role21 - pattern: "(?i)<(div|section)[^>]*(?:chat|conversation|messages)[^>]*>"22 pattern_negate: "role=\"(log|region|dialog)\""23 message: "Chat window containers must have role='log' for message history, role='region' for general chat areas, or role='dialog' for popup windows."2425 # Chat log missing accessible name26 - pattern: "(?i)<[^>]*role=\"log\"[^>]*>"27 pattern_negate: "(aria-labelledby|aria-label)"28 message: "Chat log elements must have accessible names via aria-labelledby or aria-label for screen reader identification."2930 # Chat messages missing author identification31 - pattern: "(?i)<(p|div)[^>]*(?:message|chat|conversation)[^>]*>"32 pattern_negate: "(aria-label|aria-labelledby|class.*visually-hidden|class.*sr-only)"33 message: "Chat messages must include author identification via visible text, aria-label, or visually hidden content for screen reader users."3435 # Chat messages in wrong DOM order36 - pattern: "(?i)<div[^>]*class=\"[^\"]*(?:left|right|user|agent)[^\"]*\"[^>]*>"37 pattern_negate: "(role=\"log\"|aria-live)"38 message: "Chat messages must maintain chronological order in DOM structure. Avoid using separate columns that break reading sequence."3940 # Chat notifications relying only on sound41 - pattern: "(?i)(audio|sound|play|new.*message)"42 pattern_negate: "(aria-live|role=\"status\"|visual.*notification|title.*change|notification.*badge)"43 message: "Chat notifications must use multiple sensory channels. Combine sound with visual indicators, aria-live announcements, title changes, and notification badges."4445 # Chat window missing keyboard support46 - pattern: "(?i)<(div|section)[^>]*(?:chat|conversation)[^>]*>"47 pattern_negate: "(onKeyDown|onkeydown|@keydown|v-on:keydown|tabindex)"48 message: "Chat windows must support keyboard navigation for opening, closing, and message interaction."4950 # Chat popup missing focus management51 - pattern: "(?i)<(div|section)[^>]*role=\"dialog\"[^>]*(?:chat|conversation)[^>]*>"52 pattern_negate: "(aria-modal|focus|tabindex)"53 message: "Chat popup windows must implement proper focus management and aria-modal='true' for accessibility."5455 # Chat launcher missing aria-haspopup56 - pattern: "(?i)<(button|div)[^>]*(?:chat|conversation|support)[^>]*>"57 pattern_negate: "aria-haspopup=\"dialog\""58 message: "Chat launcher buttons must include aria-haspopup='dialog' to inform users a dialog will open."5960 # Chat window missing focus trap61 - pattern: "(?i)<[^>]*role=\"dialog\"[^>]*(?:chat|conversation)[^>]*>"62 pattern_negate: "(focus.*trap|tabindex|aria-modal)"63 message: "Chat popup windows must implement focus trapping to prevent keyboard navigation outside the dialog."6465 # Chat input missing proper labeling66 - pattern: "(?i)<(input|textarea)[^>]*(?:chat|message|reply)[^>]*>"67 pattern_negate: "(aria-label|aria-labelledby|label|placeholder)"68 message: "Chat input fields must have proper labels via label elements, aria-label, or aria-labelledby for screen reader context."6970 # Chat button missing accessible name71 - pattern: "(?i)<(button|div)[^>]*(?:chat|conversation|support)[^>]*>"72 pattern_negate: "(aria-label|aria-labelledby|>.*[A-Za-z]{10,})"73 message: "Chat buttons must have accessible names via visible text, aria-label, or aria-labelledby for screen reader identification."7475 # Chat button missing proper role76 - pattern: "(?i)<(div|span)[^>]*(?:chat|conversation|support)[^>]*>"77 pattern_negate: "(role=\"button\"|<button)"78 message: "Chat controls styled as buttons must have role='button' or use native button elements for proper semantics."7980 # Chat window missing expandable state management81 - pattern: "(?i)<(button|div)[^>]*(?:chat|conversation)[^>]*>"82 pattern_negate: "aria-expanded=\"(true|false)\""83 message: "Expandable chat windows must have aria-expanded attribute to indicate open/closed state for screen readers."8485 # Chat window missing aria-controls86 - pattern: "(?i)<(button|div)[^>]*(?:chat|conversation)[^>]*>"87 pattern_negate: "aria-controls=\"[^\"]+\""88 message: "Chat controls must have aria-controls referencing the chat window ID for proper association."8990 # Chat timeout missing user notification91 - pattern: "(?i)(timeout|session.*expir|inactive)"92 pattern_negate: "(notification|warning|extend|20.*second)"93 message: "Chat timeouts must notify users before expiration and provide at least 20 seconds to extend the session."9495 # Chat auto-update missing pause controls96 - pattern: "(?i)(auto.*update|real.*time|live.*update)"97 pattern_negate: "(pause|stop|hide|user.*control)"98 message: "Auto-updating chat content must provide pause/stop controls to prevent overwhelming screen reader users."99100 # Chat missing persistent notification system101 - pattern: "(?i)<(div|section)[^>]*(?:chat|conversation)[^>]*>"102 pattern_negate: "(notification.*badge|aria-live.*polite|role.*status)"103 message: "Chat windows must provide persistent notification system including visual badge counter and persistent screen reader announcements when closed."104105 # Chat focus not contained within popup106 - pattern: "(?i)<[^>]*role=\"dialog\"[^>]*(?:chat|conversation)[^>]*>"107 pattern_negate: "(focus.*trap|tabindex|aria-modal)"108 message: "Chat popup windows must contain keyboard focus to prevent focus from becoming obscured behind the dialog."109110 # Chat messages missing proper contrast111 - pattern: "(?i)<(p|div)[^>]*(?:message|chat)[^>]*>"112 pattern_negate: "(color.*#[0-4][0-9a-fA-F]{5}|color.*#[5-9a-fA-F][0-9a-fA-F]{5})"113 message: "Chat message text must meet WCAG contrast requirements (4.5:1 minimum for normal text, 3:1 for large text)."114115 # Chat window not responsive116 - pattern: "(?i)<(div|section)[^>]*(?:chat|conversation)[^>]*>"117 pattern_negate: "(max-width|min-width|flex|grid|responsive)"118 message: "Chat windows must be responsive and work on devices down to 320px width without horizontal scrolling."119120 # Chat icon missing alt text121 - pattern: "(?i)<img[^>]*(?:chat|conversation|support)[^>]*>"122 pattern_negate: "alt=\"[^\"]+\""123 message: "Chat-related images must have descriptive alt text for screen reader users."124125 # Chat status messages missing role126 - pattern: "(?i)<(div|span)[^>]*(?:status|notification|typing)[^>]*>"127 pattern_negate: "role=\"(status|log|alert)\""128 message: "Chat status messages must use appropriate ARIA roles (status, log, or alert) for screen reader announcements."129130 - type: suggest131 message: |132 **WCAG Chat Window Accessibility Requirements:**133134 **Key Accessibility Features:**135 - **Easy to find and use:** Intuitive placement and clear labeling136 - **Multi-sensory notifications:** Visual, auditory, and haptic feedback137 - **Universal access:** All features available to all users138 - **Message preservation:** All messages accessible to everyone139140 **WCAG Criteria Implementation:**141142 **1. Info and Relationships (WCAG 1.3.1):**143 - **Author identification:** Each message must clearly indicate who sent it144 - **Visual relationships:** Use semantic markup, not just visual styling145 - **Screen reader support:** Provide context for message ownership146147 **2. Meaningful Sequence (WCAG 1.3.2):**148 - **Chronological order:** Messages must appear in DOM order matching visual order149 - **Avoid columns:** Don't separate user/agent messages into different containers150 - **Reading flow:** Maintain logical conversation sequence for screen readers151152 **3. Sensory Characteristics (WCAG 1.3.3):**153 - **Multiple notification methods:** Combine sound, visual, and screen reader announcements154 - **No single sense dependency:** Don't rely solely on audio or visual cues155 - **Universal alerts:** Ensure notifications work for all users156157 **4. Orientation (WCAG 1.3.4):**158 - **Responsive design:** Work in both portrait and landscape modes159 - **No orientation locks:** Allow users to choose their preferred orientation160 - **Flexible layouts:** Adapt to different screen sizes and orientations161162 **5. Contrast (WCAG 1.4.3):**163 - **Text contrast:** 4.5:1 minimum for normal text, 3:1 for large text164 - **Background contrast:** Ensure sufficient contrast for all text elements165 - **Visual clarity:** Make messages easily readable for all users166167 **6. Reflow (WCAG 1.4.10):**168 - **Responsive design:** Work on devices down to 320px width169 - **No horizontal scroll:** Content must fit without horizontal scrolling170 - **Flexible layouts:** Use CSS Grid and Flexbox for adaptability171172 **7. Non-text Contrast (WCAG 1.4.11):**173 - **Image alternatives:** Provide alt text for all images and icons174 - **Icon identification:** Ensure chat icons are properly labeled175 - **Visual elements:** Make all non-text content accessible176177 **8. Keyboard (WCAG 2.1.1):**178 - **Full keyboard support:** All chat functions must work with keyboard179 - **Navigation:** Support Tab, Enter, Space, and Escape keys180 - **No mouse dependency:** Ensure keyboard-only users can fully participate181182 **9. No Keyboard Trap (WCAG 2.1.2):**183 - **Focus management:** Proper focus trapping within chat windows184 - **Escape mechanism:** Provide clear way to exit chat185 - **Focus restoration:** Return focus to launcher when chat closes186 - **Focus trap:** Prevent keyboard navigation outside chat window while open187188 **11. Timing Adjustable (WCAG 2.2.1):**189 - **Session timeouts:** Notify users before session expiration (minimum 30 seconds notice)190 - **Extension options:** Provide at least 20 seconds to extend191 - **Character monitoring:** Listen for input changes, not just key presses192 - **Screen reader notification:** Use dedicated role="alert" element for important timeout warnings193194 **12. Pause, Stop, Hide (WCAG 2.2.2):**195 - **Content control:** Allow users to pause auto-updating content196 - **Overwhelm prevention:** Provide mechanisms to reduce screen reader noise197 - **Essential content:** Balance accessibility with functionality198199 **13. Enhanced Notifications (WCAG 4.1.3):**200 - **Persistent notifications:** Provide visual badge counter on chat launcher when closed201 - **Screen reader announcements:** Persistent aria-live regions for notifications when chat window is closed202 - **Multi-sensory feedback:** Combine visual, auditory, and screen reader notifications203 - **Context awareness:** Notify users about new messages even when chat window is not open204 - **Enhanced button labeling:** Dynamic accessible name that includes unread message count for better context205206 **14. User Interaction Monitoring (WCAG 2.2.1):**207 - **Session extension on interaction:** Extend chat session on any user interaction, not just sent messages208 - **Comprehensive monitoring:** Monitor clicks, key presses, input changes, scrolling, and focus events209 - **Assistive technology support:** Ensure users with typing difficulties can extend sessions through other interactions210 - **Immediate response:** Reset session timer immediately on any user engagement211212 **15. Focus Not Obscured (WCAG 2.4.11):**213 - **Focus visibility:** Ensure keyboard focus is always visible214 - **Modal containment:** Keep focus within chat popup windows215 - **Background prevention:** Don't let focus move behind chat windows216217 **16. Name, Role, Value (WCAG 4.1.2):**218 - **Accessible names:** Clear labels for all interactive elements219 - **Proper roles:** Use semantic HTML or appropriate ARIA roles220 - **State management:** Maintain and communicate current states221222 **17. Status Messages (WCAG 4.1.3):**223 - **Real-time updates:** Announce new messages and status changes224 - **Context awareness:** Provide appropriate level of detail225 - **Screen reader support:** Use role='log' for message history226227 **18. Modal Dialog Requirements:**228 - **aria-haspopup="dialog":** Set on chat launcher buttons (no aria-controls or aria-expanded needed)229 - **Focus trapping:** Prevent keyboard navigation outside chat window230 - **Initial focus:** Move focus to first focusable element when chat opens231 - **Focus restoration:** Return focus to launcher when chat closes232 - **Escape key:** Close chat window with Escape key233 - **Focus trap implementation:** Must handle both forward (Tab) and backward (Shift+Tab) navigation234 - **Focusable element detection:** Include all interactive elements (buttons, inputs, textareas, links, custom focusable elements)235 - **Safety checks:** Additional validation to ensure focus remains within chat window236237 **Implementation Patterns:**238239 **1. Basic Chat Window Structure (Modal Dialog Pattern):**240```html241 <!-- Chat launcher button -->242 <button id="chat-launcher"243 aria-haspopup="dialog">244 <img src="chat-icon.png" alt="" />245 Chat with Support246 <span id="notification-badge" class="notification-badge" hidden>0</span>247 </button>248249 **Note:** For modal dialog pattern, use only `aria-haspup="dialog"`. Do not use `aria-controls` or `aria-expanded` as these are not needed for modal dialogs.250251 <!-- Chat window -->252 <div id="chat-window"253 role="dialog"254 aria-modal="true"255 aria-labelledby="chat-heading"256 hidden>257 <div class="chat-header">258 <h2 id="chat-heading">Chat: Customer Support</h2>259 <button type="button"260 aria-label="Close chat"261 onclick="closeChat()">262 ×263 </button>264 </div>265266 <!-- Message log -->267 <div class="chat-messages"268 role="log"269 aria-labelledby="chat-heading">270 <div class="message agent" role="article">271 <div class="message-content">272 <span class="sr-only">Customer Support: </span>273 Hi, how can I help you today?274 </div>275 <time class="message-time" datetime="2024-01-15T10:00:00Z">10:00 AM</time>276 </div>277 <div class="message user" role="article">278 <div class="message-content">279 <span class="sr-only">You sent: </span>280 I'm having trouble resetting my password.281 </div>282 <time class="message-time" datetime="2024-01-15T10:01:00Z">10:01 AM</time>283 </div>284 </div>285286 <!-- Input area -->287 <div class="chat-input">288 <label for="message-input" class="visually-hidden">Type your message:</label>289 <textarea id="message-input"290 placeholder="Type your message..."291 aria-label="Type your message"></textarea>292 <button type="button"293 aria-label="Send message"294 onclick="sendMessage()">295 Send296 </button>297 </div>298 </div>299```300301 **2. Chat Message Structure:**302```html303 <!-- Good: Proper message structure with author identification -->304 <div class="chat-messages" role="log" aria-labelledby="chat-heading">305 <div class="message agent">306 <div class="message-content">307 <span class="sr-only">Customer Support: </span>308 Hi, how can I help you today?309 </div>310 <time class="message-time" datetime="2024-01-15T10:00:00Z">10:00 AM</time>311 </div>312 <div class="message user">313 <div class="message-content">314 <span class="sr-only">You sent: </span>315 I'm having trouble resetting my password.316 </div>317 <time class="message-time" datetime="2024-01-15T10:01:00Z">10:01 AM</time>318 </div>319 <div class="message agent">320 <div class="message-content">321 <span class="sr-only">Customer Support: </span>322 I can help with that. What's your email address?323 </div>324 <time class="message-time" datetime="2024-01-15T10:01:30Z">10:01 AM</time>325 </div>326 </div>327328 **Note:** Use div elements for chat messages instead of role="article" to reduce navigation noise for screen reader users. Chat messages are conversational content rather than standalone articles.329330 <!-- Bad: Separate columns that break reading order -->331 <div class="chat-columns">332 <div class="left-column">333 <p>Customer Support: Hi</p>334 <p>Customer Support: How can I help?</p>335 </div>336 <div class="right-column">337 <p>Me: Hi</p>338 <p>Me: I need help</p>339 </div>340 </div>341```342343 **3. Multi-Sensory Notifications:**344```html345 <!-- Status message for new messages -->346 <div role="status" aria-live="polite" class="chat-status">347 New message received348 </div>349350 <!-- Visual notification -->351 <div class="chat-notification" aria-hidden="true">352 <span class="notification-badge">1</span>353 </div>354355 <!-- Page title update -->356 <script>357 function updatePageTitle(message) {358 const originalTitle = document.title;359 document.title = `New message - ${originalTitle}`;360361 // Restore title after 5 seconds362 setTimeout(() => {363 document.title = originalTitle;364 }, 5000);365 }366 </script>367```368369 **4. Enhanced Notification System:**370```html371 <!-- Chat launcher with notification badge -->372 <button id="chat-launcher"373 class="chat-launcher"374 aria-haspopup="dialog">375 <img src="chat-icon.png" alt="" />376 Chat with Support377 <span id="notification-badge" class="notification-badge" hidden>0</span>378 </button>379380 **Note:** The notification badge is part of the button's accessible name, not hidden from screen readers. This provides better context about unread messages.381382 **5. Enhanced Button Labeling:**383 ```html384 <!-- Dynamic accessible name based on message count -->385 <button id="chat-launcher"386 class="chat-launcher"387 aria-haspopup="dialog">388 <img src="chat-icon.png" alt="" />389 Chat with Support390 <span id="notification-badge" class="notification-badge" hidden>0</span>391 </button>392```393394 **JavaScript for Dynamic Labeling:**395```javascript396 // Update button's accessible name to include message count397 function updateNotificationBadge() {398 const launcher = document.getElementById('chat-launcher');399400 if (messageCount === 1) {401 launcher.setAttribute('aria-label', 'Chat with Support - 1 new message');402 } else if (messageCount > 1) {403 launcher.setAttribute('aria-label', `Chat with Support - ${messageCount} new messages`);404 }405 }406407 // Reset button's accessible name when chat is opened408 function clearNotificationBadge() {409 const launcher = document.getElementById('chat-launcher');410 launcher.removeAttribute('aria-label');411 }412```413414 <!-- Persistent notification area for screen readers -->415 <div id="chat-notifications"416 role="status"417 aria-live="polite"418 class="sr-only"419 aria-label="Chat notifications">420 </div>421422 <style>423 .notification-badge {424 position: absolute;425 top: -5px;426 right: -5px;427 background: #dc3545;428 color: white;429 border-radius: 50%;430 width: 20px;431 height: 20px;432 display: flex;433 align-items: center;434 justify-content: center;435 font-size: 0.75rem;436 font-weight: bold;437 transition: all 0.3s ease;438 }439440 .notification-badge.show {441 animation: pulse 2s infinite;442 }443444 @keyframes pulse {445 0% { transform: scale(1); }446 50% { transform: scale(1.1); }447 100% { transform: scale(1); }448 }449 </style>450451 <script>452 let messageCount = 0;453454 // Update notification badge455 function updateNotificationBadge() {456 const badge = document.getElementById('notification-badge');457 const chatWindow = document.getElementById('chat-window');458 const launcher = document.getElementById('chat-launcher');459460 if (badge) {461 // Only show badge when chat is closed462 if (chatWindow.classList.contains('hidden')) {463 badge.hidden = false;464 badge.textContent = messageCount;465 badge.classList.add('show');466467 // Update button's accessible name to include message count468 if (messageCount === 1) {469 launcher.setAttribute('aria-label', 'Chat with Support - 1 new message');470 } else if (messageCount > 1) {471 launcher.setAttribute('aria-label', `Chat with Support - ${messageCount} new messages`);472 }473474 // Remove animation after 2 seconds475 setTimeout(() => {476 badge.classList.remove('show');477 }, 2000);478 } else {479 // Hide badge when chat is open480 badge.hidden = true;481 }482 }483 }484485 // Announce persistent notification for screen readers486 function announcePersistentNotification(sender) {487 const notificationArea = document.getElementById('chat-notifications');488 const chatWindow = document.getElementById('chat-window');489490 if (notificationArea && chatWindow.classList.contains('hidden')) {491 const message = `New message from ${sender === 'agent' ? 'Customer Support' : 'you'}. Chat window is closed.`;492 notificationArea.textContent = message;493494 // Clear notification after 3 seconds495 setTimeout(() => {496 notificationArea.textContent = '';497 }, 3000);498 }499 }500501 // Clear notification badge502 function clearNotificationBadge() {503 const badge = document.getElementById('notification-badge');504 const launcher = document.getElementById('chat-launcher');505506 if (badge) {507 badge.hidden = true;508 badge.classList.remove('show');509 }510511 // Reset button's accessible name512 if (launcher) {513 launcher.removeAttribute('aria-label');514 }515 }516 </script>517```518519 **6. Focus Management:**520 ```html521 <!-- Chat popup with focus management -->522 <div role="dialog"523 aria-modal="true"524 aria-labelledby="chat-heading"525 class="chat-popup">526 <div class="chat-header">527 <h2 id="chat-heading">Chat Support</h2>528 <button type="button"529 aria-label="Close chat"530 onclick="closeChat()">531 ×532 </button>533 </div>534535 <div class="chat-content" tabindex="-1">536 <!-- Chat messages and input -->537 </div>538 </div>539540 <script>541 function openChat() {542 const chatWindow = document.getElementById('chat-window');543 const launcher = document.getElementById('chat-launcher');544545 chatWindow.hidden = false;546 launcher.setAttribute('aria-expanded', 'true');547548 // Focus first focusable element in chat549 const firstFocusable = chatWindow.querySelector('button, input, textarea');550 if (firstFocusable) {551 firstFocusable.focus();552 }553 }554555 function closeChat() {556 const chatWindow = document.getElementById('chat-window');557 const launcher = document.getElementById('chat-launcher');558559 chatWindow.hidden = true;560 launcher.setAttribute('aria-expanded', 'false');561562 // Return focus to launcher563 launcher.focus();564 }565 </script>566```567568 **7. Session Timeout Management:**569```html570 <!-- Timeout warning -->571 <div class="timeout-warning" hidden>572 <p>This chat will end in <span id="timeout-countdown">20</span> seconds if you do not reply.</p>573 <button type="button" onclick="extendSession()">Extend Session</button>574 </div>575576 <!-- Timeout alert for screen readers -->577 <div role="alert" aria-live="assertive" class="timeout-alert" hidden>578 This chat will end in 20 seconds if you do not reply.579 </div>580581 <script>582 let sessionTimeout;583 let warningTimeout;584585 function startSessionTimer() {586 // Start 5-minute session timer587 sessionTimeout = setTimeout(() => {588 showTimeoutWarning();589 }, 280000); // 4 minutes 40 seconds590 }591592 function showTimeoutWarning() {593 const warning = document.querySelector('.timeout-warning');594 const countdown = document.getElementById('timeout-countdown');595 const timeoutAlert = document.querySelector('.timeout-alert');596597 warning.hidden = false;598 timeoutAlert.hidden = false;599600 // 20-second countdown601 let timeLeft = 20;602 const countdownInterval = setInterval(() => {603 timeLeft--;604 countdown.textContent = timeLeft;605606 if (timeLeft <= 0) {607 clearInterval(countdownInterval);608 endSession();609 }610 }, 1000);611 }612613 function extendSession() {614 // Set session as extended FIRST to prevent any race conditions615 isSessionExtended = true;616617 const warning = document.querySelector('.timeout-warning');618 const timeoutAlert = document.querySelector('.timeout-alert');619620 // Clear any existing warning timeout621 if (warningTimeout) {622 clearInterval(warningTimeout);623 warningTimeout = null;624 }625626 // Clear the main session timeout as well627 if (sessionTimeout) {628 clearTimeout(sessionTimeout);629 sessionTimeout = null;630 }631632 // Hide warning elements633 if (warning) warning.hidden = true;634 if (timeoutAlert) timeoutAlert.hidden = true;635636 // Start fresh session timer637 startSessionTimer();638 }639640 // Extend session on user interaction641 function extendSessionOnInteraction() {642 // Only extend if warning is currently showing643 const warning = document.querySelector('.timeout-warning');644 if (warning && !warning.hidden) {645 extendSession();646 } else {647 // Just reset the session timer648 clearTimeout(sessionTimeout);649 clearInterval(warningTimeout); // Also clear any warning timeout that might be running650 isSessionExtended = true; // Mark session as extended651 startSessionTimer();652 }653 }654655 // Monitor input for activity656 document.getElementById('message-input').addEventListener('input', () => {657 // Reset timer on any input658 clearTimeout(sessionTimeout);659 startSessionTimer();660 });661662 // Setup comprehensive user interaction monitoring663 function setupUserInteractionListeners() {664 const chatWindow = document.getElementById('chat-window');665666 // Add listeners for various user interactions667 chatWindow.addEventListener('click', extendSessionOnInteraction);668 chatWindow.addEventListener('keydown', extendSessionOnInteraction);669 chatWindow.addEventListener('keypress', extendSessionOnInteraction);670 chatWindow.addEventListener('input', extendSessionOnInteraction);671 chatWindow.addEventListener('scroll', extendSessionOnInteraction);672673 // Also listen for focus events on interactive elements674 const interactiveElements = chatWindow.querySelectorAll('button, input, textarea, [tabindex]:not([tabindex="-1"])');675 interactiveElements.forEach(element => {676 element.addEventListener('focus', extendSessionOnInteraction);677 });678 }679 </script>680```681682 **Note:** The timeout warning should appear with sufficient notice (minimum 30 seconds) and use a dedicated `role="alert"` element for screen reader announcements. This ensures important timeout information is properly communicated without interfering with chat functionality.683684 **8. User Interaction Monitoring:**685```javascript686 // Setup comprehensive user interaction monitoring687 function setupUserInteractionListeners() {688 const chatWindow = document.getElementById('chat-window');689690 // Add listeners for various user interactions691 chatWindow.addEventListener('click', extendSessionOnInteraction);692 chatWindow.addEventListener('keydown', extendSessionOnInteraction);693 chatWindow.addEventListener('keypress', extendSessionOnInteraction);694 chatWindow.addEventListener('input', extendSessionOnInteraction);695 chatWindow.addEventListener('scroll', extendSessionOnInteraction);696697 // Also listen for focus events on interactive elements698 const interactiveElements = chatWindow.querySelectorAll('button, input, textarea, [tabindex]:not([tabindex="-1"])');699 interactiveElements.forEach(element => {700 element.addEventListener('focus', extendSessionOnInteraction);701 });702 }703```704705 **Note:** Chat sessions should extend on any user interaction, not just sent messages. This is crucial for assistive technology users who may have difficulty typing or need more time to compose messages. Monitor clicks, key presses, input changes, scrolling, and focus events to ensure the session remains active during user engagement.706707 **Important:** When extending sessions, ensure both the main session timeout and warning timeout are properly cleared to prevent race conditions where the session can end even after being extended.708709 **Session State Management:** Use a dedicated boolean flag (`isSessionExtended`) to track session extension state, rather than relying on DOM element visibility. This prevents race conditions and ensures reliable session management.710711 **Race Condition Prevention:** Set the session extension flag BEFORE clearing timeouts to prevent race conditions where the countdown might still call `endSession()` after the session has been extended.712713 **Countdown Safety:** Implement proper bounds checking to prevent negative countdown values and ensure the countdown stops immediately when the session is extended. Clear existing intervals before starting new ones to prevent multiple countdowns from running simultaneously.714715 **Session Ending Protection:** Implement multiple safety checks in the `endSession()` function to prevent premature session termination. Check session extension status, session activity state, and warning visibility before allowing the session to end.716717 **Visual Design Consistency:** Follow the established design pattern used across all accessibility example files, including consistent container styling, typography, color scheme, and layout structure for a cohesive user experience.718719 **9. Responsive Chat Design:**720```css721 /* Responsive chat window */722 .chat-window {723 position: fixed;724 bottom: 20px;725 right: 20px;726 width: 350px;727 height: 500px;728 max-width: calc(100vw - 40px);729 max-height: calc(100vh - 40px);730 background: white;731 border: 1px solid #ccc;732 border-radius: 8px;733 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);734 display: flex;735 flex-direction: column;736 }737738 /* Mobile responsive adjustments */739 @media screen and (max-width: 480px) {740 .chat-window {741 bottom: 0;742 right: 0;743 width: 100%;744 height: 100vh;745 max-width: none;746 max-height: none;747 border-radius: 0;748 }749 }750751 /* Ensure minimum width compliance */752 @media screen and (max-width: 320px) {753 .chat-window {754 width: 320px;755 min-width: 320px;756 }757 }758```759760 **10. Screen Reader Optimized Messages:**761```html762 <!-- Message with proper structure -->763 <div class="message agent" role="article">764 <div class="message-content">765 <span class="sr-only">Customer Support: </span>766 Hi, how can I help you today?767 </div>768 <time datetime="2024-01-15T10:30:00Z" class="timestamp">769 <span class="sr-only">Message sent at </span>770 10:30 AM771 </time>772 </div>773774 <!-- Typing indicator -->775 <div class="typing-indicator" aria-live="polite">776 <span class="visually-hidden">Customer Support is typing</span>777 <span class="typing-dots">...</span>778 </div>779```780781 **11. Proper Focus Trap Implementation:**782```javascript783 // Focus trap variables784 let focusableElements = [];785 let firstFocusableElement = null;786 let lastFocusableElement = null;787788 // Setup focus trap789 function setupFocusTrap() {790 const chatWindow = document.getElementById('chat-window');791792 // Get all focusable elements within the chat window793 // Include all interactive elements that can receive focus794 focusableElements = chatWindow.querySelectorAll(795 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"]), [role="button"]:not([disabled])'796 );797798 if (focusableElements.length > 0) {799 firstFocusableElement = focusableElements[0];800 lastFocusableElement = focusableElements[focusableElements.length - 1];801802 // Add focus trap event listener803 chatWindow.addEventListener('keydown', trapFocus);804 }805 }806807 // Focus trap functionality808 function trapFocus(event) {809 if (event.key === 'Tab') {810 // Check if focus is currently within the chat window811 const chatWindow = document.getElementById('chat-window');812 const isFocusInChat = chatWindow.contains(document.activeElement);813814 if (event.shiftKey) {815 // Shift + Tab: move backwards816 if (document.activeElement === firstFocusableElement) {817 event.preventDefault();818 lastFocusableElement.focus();819 }820 } else {821 // Tab: move forwards822 if (document.activeElement === lastFocusableElement) {823 event.preventDefault();824 firstFocusableElement.focus();825 }826 }827828 // Additional safety check: if focus somehow escapes, bring it back829 if (!isFocusInChat && focusableElements.length > 0) {830 event.preventDefault();831 firstFocusableElement.focus();832 }833 }834 }835836 // Clean up focus trap when closing chat837 function closeChat() {838 const chatWindow = document.getElementById('chat-window');839 const launcher = document.getElementById('chat-launcher');840841 chatWindow.hidden = true;842 launcher.setAttribute('aria-expanded', 'false');843844 // Remove focus trap845 chatWindow.removeEventListener('keydown', trapFocus);846847 // Return focus to launcher848 launcher.focus();849 }850```851852 **JavaScript Considerations:**853 - Implement proper focus management for popup windows854 - Handle keyboard navigation (Tab, Enter, Escape)855 - Manage session timeouts with user notifications856 - Provide pause/stop controls for auto-updating content857 - Update page titles for new message notifications858 - Monitor input activity for session extension859 - Handle focus restoration when chat closes860 - Implement proper ARIA live regions for status updates861862 **Accessibility Notes:**863 - Use role="log" for message history with aria-labelledby864 - Provide multi-sensory notifications for new messages865 - Maintain chronological message order in DOM866 - Ensure all interactive elements have accessible names867 - Test with screen readers for proper announcement868 - Consider user preferences for notification levels869 - Provide alternatives to chat (phone, email, etc.)870 - Test keyboard navigation thoroughly871 - Validate focus management in popup windows872 - Use div elements for chat messages to reduce navigation noise (avoid role="article")873 - Chat messages are conversational content, not standalone articles874875 **Testing Requirements:**876 - Test with screen readers (NVDA, JAWS, VoiceOver)877 - Verify keyboard navigation works completely878 - Test focus management in popup windows879 - Validate message reading order880 - Test timeout notifications and extensions881 - Verify responsive design on small screens882 - Test with different notification preferences883 - Validate ARIA live region announcements884 - Test focus restoration when chat closes885 - Verify contrast ratios meet WCAG requirements886 - **Test focus trapping thoroughly:** Verify Tab and Shift+Tab navigation stays within chat window887 - **Test focus escape scenarios:** Ensure focus cannot accidentally leave the chat window888 - **Test focus restoration:** Verify focus returns to launcher when chat closes889 - **Test all interactive elements:** Ensure all buttons, inputs, and focusable elements are included in focus trap890891 **Common Mistakes to Avoid:**892 - Chat messages in wrong DOM order893 - Notifications relying only on sound894 - Missing keyboard support for chat functions895 - Poor focus management in popup windows896 - Missing author identification in messages897 - Chat windows not responsive to small screens898 - Missing timeout warnings and extensions899 - Auto-updating content without pause controls900 - Focus becoming obscured behind chat windows901 - Missing accessible names for chat controls902 - Chat buttons without proper roles903 - Missing expandable state management904 - Insufficient contrast for message text905 - Chat icons without alt text906 - Missing status message roles907 - Missing aria-haspopup="dialog" on chat launcher908 - Missing focus trapping in chat popup windows909 - Poor focus management when opening/closing chat910 - **Incomplete focus trap implementation:** Focus trap that only works in one direction (Tab vs Shift+Tab)911 - **Missing focusable elements:** Focus trap that doesn't include all interactive elements912 - **Focus escape vulnerabilities:** Focus trap without safety checks to prevent focus from leaving chat window913 - **Incorrect ARIA attributes for modal dialogs:** Using aria-controls or aria-expanded with modal dialog pattern914 - **Missing notification context:** Not including unread message count in button's accessible name915 - **Decorative images with alt text:** Including unnecessary alt text for decorative icons in buttons916 - **Limited session extension:** Only extending chat session on sent messages instead of all user interactions917 - **Missing user interaction monitoring:** Not monitoring clicks, key presses, scrolling, and focus events for session extension918919metadata:920 priority: high921 version: 1.0922</rule>923description:924globs:925alwaysApply: false926---927
Also in Shopify/horizon
Diff this repo’s formatsOne repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| Shopify/horizon.cursor/rules/accordion-accessibility.mdc · 428 | Cursor rules | ui | 40/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/animation-accessibility.mdc · 428 | Cursor rules | ui | 40/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/blocks.mdc · 428 | Cursor rules | stylearchtypesdatabase+2 | 62/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/breadcrumb-accessibility.mdc · 428 | Cursor rules | ui | 36/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/carousel-accessibility.mdc · 428 | Cursor rules | ui | 32/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/cart-drawer-accessibility.mdc · 428 | Cursor rules | ui | 40/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/color-contrast-accessibility.mdc · 428 | Cursor rules | lint-formatui | 44/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/color-swatch-accessibility.mdc · 428 | Cursor rules | ui | 40/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/commit-messages.mdc · 428 | Cursor rules | setuplint-formattypesgit | 62/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/css-standards.mdc · 428 | Cursor rules | stylearchuiperformance+3 | 49/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/disclosure-accessibility.mdc · 428 | Cursor rules | ui | 40/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/dropdown-navigation-accessibility.mdc · 428 | Cursor rules | styleui | 36/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/flip-card-accessibility.mdc · 428 | Cursor rules | styleui | 36/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/form-accessibility.mdc · 428 | Cursor rules | ui | 32/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/javascript-standards.mdc · 428 | Cursor rules | stylearchtypesui+2 | 54/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/landmark-accessibility.mdc · 428 | Cursor rules | ui | 40/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/liquid.mdc · 428 | Cursor rules | buildstyletypesdatabase+2 | 77/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/locales.mdc · 428 | Cursor rules | arch | 49/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/localization.mdc · 428 | Cursor rules | style | 54/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/mobile-accessibility-standards.mdc · 428 | Cursor rules | styleui | 36/100 | 3 days ago |
Diff against .cursor/rules/accordion-accessibility.mdc Diff against .cursor/rules/animation-accessibility.mdc Diff against .cursor/rules/blocks.mdc Diff against .cursor/rules/breadcrumb-accessibility.mdc Diff against .cursor/rules/carousel-accessibility.mdc Diff against .cursor/rules/cart-drawer-accessibility.mdc Diff against .cursor/rules/color-contrast-accessibility.mdc Diff against .cursor/rules/color-swatch-accessibility.mdc Diff against .cursor/rules/commit-messages.mdc Diff against .cursor/rules/css-standards.mdc Diff against .cursor/rules/disclosure-accessibility.mdc Diff against .cursor/rules/dropdown-navigation-accessibility.mdc Diff against .cursor/rules/flip-card-accessibility.mdc Diff against .cursor/rules/form-accessibility.mdc Diff against .cursor/rules/javascript-standards.mdc Diff against .cursor/rules/landmark-accessibility.mdc Diff against .cursor/rules/liquid.mdc Diff against .cursor/rules/locales.mdc Diff against .cursor/rules/localization.mdc Diff against .cursor/rules/mobile-accessibility-standards.mdc
