{"id":"12eb9d32-a03d-433c-84b2-82e6d7a334b2","shortId":"J6dK6j","kind":"skill","title":"cometchat-a11y","tagline":"Accessibility (a11y) for CometChat UI Kit integrations across all families — React, React Native, Angular, Android (V5/V6), iOS, Flutter. Covers WCAG 2.1 AA targets, keyboard navigation in chat, screen reader announcements (live regions for new messages), color contrast, focus ma","description":"## Purpose\n\nAccessibility for CometChat integrations. Out-of-the-box, the UI Kit components are mostly accessible — the kit's own buttons, inputs, and lists ship with semantic markup. Production gaps appear in the wiring **around** the kit: custom call surfaces, navigation, focus management on screen transitions, and contrast in custom themes.\n\nTarget: **WCAG 2.1 AA**. The skill writes code that meets this baseline.\n\n---\n\n## The five gaps that trip integrations (any family)\n\n1. **Color contrast in custom themes.** A brand color picked for \"looks nice in the brand book\" may be 3.2:1 against the text — fails AA's 4.5:1 minimum.\n2. **Focus management on chat screen entry.** Tab/screen reader user lands on the chat screen but focus stays on the previous trigger button. They have to manually navigate into the message list every time.\n3. **No live region announcement for new messages.** Screen reader users don't know a new message arrived unless they navigate the message list and hear the new item.\n4. **Keyboard-only users can't navigate the conversation list.** Click handlers bound to `<div>` instead of `<button>` skip keyboard events.\n5. **Reduced-motion users see decorative animations.** Typing-indicator dots, message bubble entrance animations, transition effects — should respect `prefers-reduced-motion`.\n\nThis skill addresses each one across families.\n\n---\n\n## 1. Color contrast — the theme audit\n\nCometChat themes are CSS variables (web/RN) or color tokens (native/Flutter). Override a single color and you might fail AA.\n\n### Web / Angular — CSS variable contrast check\n\n```ts\n// scripts/check-contrast.ts (run in CI or as a one-shot)\nfunction contrastRatio(hex1: string, hex2: string): number {\n  const luminance = (hex: string) => {\n    const rgb = hex.match(/\\w\\w/g)!.map(c => parseInt(c, 16) / 255).map(c =>\n      c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4\n    );\n    return 0.2126 * rgb[0] + 0.7152 * rgb[1] + 0.0722 * rgb[2];\n  };\n  const l1 = luminance(hex1);\n  const l2 = luminance(hex2);\n  return (Math.max(l1, l2) + 0.05) / (Math.min(l1, l2) + 0.05);\n}\n\n// Pull the values from your CSS variables\nconst fg = getComputedStyle(document.documentElement).getPropertyValue(\"--cometchat-text-color\").trim();\nconst bg = getComputedStyle(document.documentElement).getPropertyValue(\"--cometchat-background-color\").trim();\nconst ratio = contrastRatio(fg, bg);\nif (ratio < 4.5) {\n  console.warn(`Text/background contrast ${ratio.toFixed(2)}:1 fails WCAG AA (need ≥4.5:1)`);\n}\n```\n\nIn CI, add this to your test suite. The skill writes a starter version into `tests/a11y/contrast.test.ts`.\n\n### React Native / native / Flutter — manual audit at theme-design time\n\nUse a contrast-checker tool (browser extensions, https://webaim.org/resources/contrastchecker/) on the theme tokens before shipping. There's no runtime DOM to audit on native.\n\nThe kit's default theme tokens pass AA. Custom palettes need the audit.\n\n**Common fail:** brand purple #6750A4 against white background = 6.6:1 (passes). Same purple against `#F0F0F0` light gray = 5.7:1 (passes). Same purple against `#999999` muted gray = 2.8:1 (FAILS). Watch for muted backgrounds in dark-mode toggles, secondary buttons, and \"subtle\" surfaces.\n\n---\n\n## 2. Focus management on chat screen entry\n\nWhen the user navigates to a chat screen (clicked a conversation, opened the chat tab, accepted a deep link), focus should land on a meaningful control — usually the message composer or the latest message.\n\n### React (web)\n\n```tsx\nimport { useEffect, useRef } from \"react\";\n\nexport function ChatScreen() {\n  const composerRef = useRef<HTMLElement>(null);\n\n  useEffect(() => {\n    // After mount + animations, focus the composer\n    const timer = setTimeout(() => {\n      composerRef.current?.focus();\n    }, 100);\n    return () => clearTimeout(timer);\n  }, []);\n\n  return (\n    <div>\n      <CometChatMessageHeader />\n      <CometChatMessageList />\n      <CometChatMessageComposer ref={composerRef} />\n    </div>\n  );\n}\n```\n\nThe kit's `CometChatMessageComposer` accepts a forwarded ref in v6; if not, query for the input via `composerRef.current?.querySelector(\"input, [contenteditable]\")?.focus()`.\n\n### React Native\n\n```tsx\nimport { useRef, useEffect } from \"react\";\nimport { findNodeHandle, AccessibilityInfo } from \"react-native\";\n\nexport function ChatScreen() {\n  const composerRef = useRef(null);\n\n  useEffect(() => {\n    const handle = findNodeHandle(composerRef.current);\n    if (handle) {\n      AccessibilityInfo.setAccessibilityFocus(handle);\n    }\n  }, []);\n\n  return (\n    <View>\n      <CometChatMessageHeader />\n      <CometChatMessageList />\n      <CometChatMessageComposer ref={composerRef} />\n    </View>\n  );\n}\n```\n\n### Angular\n\n```ts\n@Component({...})\nexport class ChatComponent implements AfterViewInit {\n  @ViewChild(\"composer\") composer!: ElementRef;\n\n  ngAfterViewInit() {\n    setTimeout(() => this.composer.nativeElement.focus(), 100);\n  }\n}\n```\n\n### Native Android (Kotlin)\n\n```kotlin\noverride fun onResume() {\n  super.onResume()\n  composerView.requestFocus()\n  composerView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)\n}\n```\n\n### Native iOS (Swift)\n\n```swift\noverride func viewDidAppear(_ animated: Bool) {\n  super.viewDidAppear(animated)\n  UIAccessibility.post(notification: .screenChanged, argument: composerView)\n}\n```\n\n### Flutter\n\n```dart\nfinal FocusNode _composerFocus = FocusNode();\n\n@override\nvoid initState() {\n  super.initState();\n  WidgetsBinding.instance.addPostFrameCallback((_) {\n    _composerFocus.requestFocus();\n  });\n}\n\n// Then on the composer widget: focusNode: _composerFocus\n```\n\n---\n\n## 3. Live region for new messages\n\nScreen reader users need an audible announcement when a new message arrives — otherwise they have to navigate to the message list and re-read it.\n\n### Web / Angular — ARIA live region\n\n```html\n<!-- A visually-hidden region that screen readers announce -->\n<div\n  aria-live=\"polite\"\n  aria-atomic=\"true\"\n  style=\"position: absolute; left: -9999px; height: 1px; width: 1px; overflow: hidden;\"\n  id=\"message-announcer\"></div>\n```\n\n```ts\n// Listen for new messages and announce\nimport { CometChat } from \"@cometchat/chat-sdk-javascript\";\n\nconst listenerId = \"a11y-message-announcer\";\nCometChat.addMessageListener(listenerId, new CometChat.MessageListener({\n  onTextMessageReceived: (msg: CometChat.TextMessage) => {\n    const senderName = msg.getSender().getName();\n    const text = msg.getText();\n    const region = document.getElementById(\"message-announcer\");\n    if (region) {\n      // Clearing first ensures the same text re-announces\n      region.textContent = \"\";\n      setTimeout(() => {\n        region.textContent = `New message from ${senderName}: ${text}`;\n      }, 100);\n    }\n  },\n}));\n```\n\n`aria-live=\"polite\"` waits for the user to finish speaking before announcing. Use `aria-live=\"assertive\"` only for urgent messages (like incoming calls) — too aggressive for chat.\n\n### React Native\n\n```ts\nimport { AccessibilityInfo } from \"react-native\";\n\nCometChat.addMessageListener(listenerId, new CometChat.MessageListener({\n  onTextMessageReceived: (msg) => {\n    const text = `New message from ${msg.getSender().getName()}: ${msg.getText()}`;\n    AccessibilityInfo.announceForAccessibility(text);\n  },\n}));\n```\n\n### Native Android / iOS / Flutter\n\nEach platform has an equivalent — Android `View.announceForAccessibility(text)`, iOS `UIAccessibility.post(notification: .announcement, argument: text)`, Flutter `SemanticsService.announce(text, TextDirection.ltr)`. Same shape; the SDK callback is the trigger.\n\n---\n\n## 4. Keyboard navigation\n\nThe kit's components are keyboard-accessible by default. Custom wrapping is what breaks it.\n\n### Anti-pattern — `<div onClick>` for clickable items\n\n```tsx\n// ✗ WRONG — keyboard users can't activate\n<div onClick={() => openConversation(c)}>{c.name}</div>\n\n// ✓ RIGHT — `<button>` is keyboard + screen-reader native\n<button onClick={() => openConversation(c)}>{c.name}</button>\n\n// ✓ ALSO RIGHT — div with explicit ARIA + keyboard handlers\n<div\n  role=\"button\"\n  tabIndex={0}\n  onClick={() => openConversation(c)}\n  onKeyDown={(e) => {\n    if (e.key === \"Enter\" || e.key === \" \") {\n      e.preventDefault();\n      openConversation(c);\n    }\n  }}\n>\n  {c.name}\n</div>\n```\n\n### Skip links\n\nFor long conversation lists, add a skip-to-message-composer link:\n\n```html\n<a href=\"#message-composer\" class=\"skip-link\">Skip to message composer</a>\n```\n\n```css\n.skip-link {\n  position: absolute;\n  left: -9999px;\n  z-index: 999;\n}\n.skip-link:focus {\n  left: 0;\n  top: 0;\n  background: white;\n  padding: 8px;\n}\n```\n\nThe kit's components already include skip links where applicable; custom wrapping should preserve them.\n\n### Keyboard shortcuts\n\nFor productivity apps:\n\n```ts\nuseEffect(() => {\n  const handler = (e: KeyboardEvent) => {\n    // Cmd/Ctrl + K → focus search\n    if ((e.metaKey || e.ctrlKey) && e.key === \"k\") {\n      e.preventDefault();\n      searchRef.current?.focus();\n    }\n    // Escape → close any open thread / modal\n    if (e.key === \"Escape\") {\n      closeOpenThread();\n    }\n  };\n  window.addEventListener(\"keydown\", handler);\n  return () => window.removeEventListener(\"keydown\", handler);\n}, []);\n```\n\nDocument the shortcuts in your in-app help — discoverability matters.\n\n---\n\n## 5. Reduced motion\n\nAnimations help most users; they cause physical discomfort or distraction for users with vestibular disorders, ADHD, or who simply prefer less movement. WCAG 2.1 AA requires honoring the OS preference.\n\n### Web / Angular — CSS\n\n```css\n@media (prefers-reduced-motion: reduce) {\n  /* Disable kit animations + your custom ones */\n  * {\n    animation-duration: 0.01ms !important;\n    animation-iteration-count: 1 !important;\n    transition-duration: 0.01ms !important;\n  }\n}\n```\n\n### React Native\n\n```ts\nimport { AccessibilityInfo } from \"react-native\";\n\nconst [reduceMotion, setReduceMotion] = useState(false);\n\nuseEffect(() => {\n  AccessibilityInfo.isReduceMotionEnabled().then(setReduceMotion);\n  const sub = AccessibilityInfo.addEventListener(\"reduceMotionChanged\", setReduceMotion);\n  return () => sub.remove();\n}, []);\n\n// In animations\n<Animated.View\n  style={{\n    transform: [{ scale: reduceMotion ? 1 : animatedValue }],\n  }}\n/>\n```\n\n### Native iOS\n\n```swift\nlet reduceMotion = UIAccessibility.isReduceMotionEnabled\nif !reduceMotion {\n  UIView.animate(withDuration: 0.3) { ... }\n} else {\n  // Apply final state without animation\n}\n```\n\n### Native Android\n\n```kotlin\nval reduceMotion = Settings.Global.getFloat(\n  contentResolver, Settings.Global.ANIMATOR_DURATION_SCALE, 1.0f\n) == 0.0f\n```\n\n### Flutter\n\n```dart\nfinal reduceMotion = MediaQuery.of(context).disableAnimations;\n```\n\nThe skill defaults to wrapping the typing indicator + message-bubble entrance animations in reduce-motion guards. Other kit animations need similar treatment if you've customized them.\n\n---\n\n## Calls a11y\n\nCalls have specific a11y considerations beyond chat:\n\n1. **Focus on call screen entry → end-call button.** Avoids accidental hangup but ensures the user can quickly exit.\n2. **Announce incoming calls.** Live region (web/RN) or `UIAccessibility.post(.announcement)` (iOS) — \"Incoming call from Alice.\"\n3. **Mute/end button labels.** \"Mute microphone\" not just \"Mute\" — clarify what's being toggled.\n4. **No flashing — recording indicator uses dot, not strobe.** WCAG 2.3 forbids more than 3 flashes/sec to prevent seizures.\n5. **Caption support if recording.** Production calling apps with live captioning hook in via the platform's speech-recognition API and overlay text on the call screen.\n\n---\n\n## Testing — automated + manual\n\n### Web automated\n\n```bash\nnpm install --save-dev @axe-core/playwright\n```\n\n```ts\nimport { test, expect } from \"@playwright/test\";\nimport AxeBuilder from \"@axe-core/playwright\";\n\ntest(\"chat screen passes axe AA\", async ({ page }) => {\n  await page.goto(\"/messages\");\n  const results = await new AxeBuilder({ page }).withTags([\"wcag2a\", \"wcag2aa\"]).analyze();\n  expect(results.violations).toEqual([]);\n});\n```\n\n### Native automated\n\niOS: Xcode → Accessibility Inspector → Audit. Android: Accessibility Scanner app on a real device.\n\n### Manual\n\n- **Keyboard-only navigation** — unplug your mouse, complete a full chat flow. Tab through every control.\n- **VoiceOver (iOS) / TalkBack (Android) / NVDA (Windows) / VoiceOver (macOS)** — listen to the kit; verify announcements make sense.\n- **Browser zoom 200%** — kit should remain usable at 200% zoom on a 1280×720 viewport (WCAG 1.4.10 reflow).\n\n---\n\n## Anti-patterns\n\n1. **Custom themes without a contrast audit.** Brand colors silently fail AA.\n2. **`<div onClick>` for clickable items.** Keyboard users can't activate.\n3. **No live region for new messages.** Screen reader users miss messages.\n4. **Auto-playing video on call screen.** Some users browse with autoplay disabled — kit handles this; custom UI must too.\n5. **Focus stays on the trigger button after opening chat.** User has to manually re-navigate.\n6. **Ignoring `prefers-reduced-motion`.** Vestibular-disorder users get disoriented.\n7. **`aria-live=\"assertive\"` for chat messages.** Interrupts the user mid-sentence; reserve for genuinely urgent (incoming call).\n8. **Skipping label specificity.** \"Mute\" alone is ambiguous; \"Mute microphone\" / \"Unmute microphone\" is clear.\n9. **Color-only signals.** \"Read\" status as just a checkmark color — add a visible text label for color-blind users.\n10. **No test coverage for a11y.** Regressions slip in. Automate the easy checks (axe-core); manual-test the rest per release.\n\n---\n\n## Verification checklist\n\n**Cross-family:**\n- [ ] Custom theme passes AA contrast (4.5:1 text, 3:1 large text + UI components)\n- [ ] Focus lands on a meaningful control on chat screen entry\n- [ ] Live region / accessibility announcement on new message receive\n- [ ] No `<div onClick>` patterns — buttons are buttons\n- [ ] `prefers-reduced-motion` / `isReduceMotionEnabled` honored for animations\n- [ ] Mute/end/camera labels are specific (not just \"Mute\")\n- [ ] Incoming-call announcement (`assertive` live region OR platform announcement API)\n- [ ] No flashing > 3 Hz (recording dot uses fade, not strobe)\n\n**Web/Angular:**\n- [ ] axe-core / Playwright a11y test in CI; passes WCAG AA tags\n- [ ] `<html lang=\"...\">` set to current locale (consumes `cometchat-i18n` skill output)\n- [ ] Skip-to-composer link present\n- [ ] Browser zoom 200% smoke test\n\n**Native (Android/iOS/Flutter):**\n- [ ] TalkBack / VoiceOver smoke test on a real device\n- [ ] Reduced motion preference observed (`UIAccessibility.isReduceMotionEnabled`, etc.)\n- [ ] Focus restored on screen pop (not just push)\n- [ ] Touch targets ≥ 44×44 pt (iOS) / 48×48 dp (Android) — kit defaults pass; custom UI must too\n\n---\n\n## Pointers\n\n- `cometchat-i18n` — sister skill; `<html lang>` and screen-reader pronunciation of translated strings depend on locale set correctly\n- `cometchat-{family}-customization` — when customizing kit components, preserve their built-in a11y attributes\n- `cometchat-{family}-troubleshooting` — when a11y tools report issues that look kit-internal\n- WCAG 2.1 AA reference — https://www.w3.org/WAI/WCAG21/quickref/?levels=aa\n- Material Design accessibility — https://m3.material.io/foundations/accessible-design\n- Apple HIG accessibility — https://developer.apple.com/design/human-interface-guidelines/accessibility","tags":["cometchat","a11y","skills","agent-skills","ai-agent","chat","claude-code","cursor","messaging","nextjs","react","react-native"],"capabilities":["skill","source-cometchat","skill-cometchat-a11y","topic-agent-skills","topic-ai-agent","topic-chat","topic-claude-code","topic-cometchat","topic-cursor","topic-messaging","topic-nextjs","topic-react","topic-react-native","topic-ui-kit"],"categories":["cometchat-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/cometchat/cometchat-skills/cometchat-a11y","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add cometchat/cometchat-skills","source_repo":"https://github.com/cometchat/cometchat-skills","install_from":"skills.sh"}},"qualityScore":"0.463","qualityRationale":"deterministic score 0.46 from registry signals: · indexed on github topic:agent-skills · 27 github stars · SKILL.md body (15,393 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T19:04:44.433Z","embedding":null,"createdAt":"2026-05-18T07:04:15.674Z","updatedAt":"2026-05-18T19:04:44.433Z","lastSeenAt":"2026-05-18T19:04:44.433Z","tsv":"'-9999':993 '/design/human-interface-guidelines/accessibility':1850 '/foundations/accessible-design':1844 '/messages':1388 '/playwright':1364,1377 '/resources/contrastchecker/)':444 '/wai/wcag21/quickref/?levels=aa':1838 '0':336,953,1004,1006 '0.0':1207 '0.01':1129,1141 '0.03928':326 '0.05':355,359 '0.055':330 '0.0722':340 '0.2126':334 '0.3':1188 '0.7152':337 '1':115,135,143,259,339,400,406,482,491,500,1136,1176,1254,1471,1644,1647 '1.0':1205 '1.055':331 '1.4.10':1466 '10':1610 '100':584,664,807 '12.92':328 '1280':1462 '16':321 '2':145,342,399,516,1274,1483 '2.1':24,97,1103,1833 '2.3':1313 '2.4':332 '2.8':499 '200':1452,1458,1742 '255':322 '3':179,713,1289,1317,1492,1646,1703 '3.2':134 '4':208,892,1303,1504 '4.5':142,394,405,1643 '44':1771,1772 '48':1775,1776 '5':228,1077,1322,1525 '5.7':490 '6':1542 '6.6':481 '6750a4':477 '7':1554 '720':1463 '8':1574 '8px':1010 '9':1588 '999':998 '999999':496 'a11y':3,5,765,1246,1250,1615,1716,1817,1823 'a11y-message-announcer':764 'aa':25,98,140,283,403,467,1104,1383,1482,1641,1722,1834 'absolut':991 'accept':538,596 'access':4,44,59,902,1406,1410,1664,1841,1847 'accessibilityevent.type':675 'accessibilityinfo':624,841,1148 'accessibilityinfo.addeventlistener':1164 'accessibilityinfo.announceforaccessibility':860 'accessibilityinfo.isreducemotionenabled':1159 'accessibilityinfo.setaccessibilityfocus':643 'accident':1265 'across':11,257 'activ':923,1491 'add':409,973,1600 'address':254 'adhd':1095 'afterviewinit':656 'aggress':834 'alic':1288 'alon':1579 'alreadi':1015 'also':941 'ambigu':1581 'analyz':1398 'android':18,666,863,871,1196,1409,1437,1778 'android/ios/flutter':1746 'angular':17,285,649,746,1111 'anim':235,243,575,685,688,1080,1122,1127,1133,1170,1194,1228,1236,1682 'animated.view':1171 'animatedvalu':1177 'animation-dur':1126 'animation-iteration-count':1132 'announc':33,183,725,757,767,787,798,820,877,1275,1283,1447,1665,1693,1699 'anti':912,1469 'anti-pattern':911,1468 'api':1342,1700 'app':1030,1073,1329,1412 'appear':74 'appl':1845 'appli':1190 'applic':1020 'argument':692,878 'aria':747,809,823,946,1556 'aria-l':808,822,1555 'around':78 'arriv':196,730 'assert':825,1558,1694 'async':1384 'attribut':1818 'audibl':724 'audit':264,428,457,472,1408,1477 'auto':1506 'auto-play':1505 'autom':1351,1354,1403,1619 'autoplay':1516 'avoid':1264 'await':1386,1391 'axe':1362,1375,1382,1624,1713 'axe-cor':1361,1374,1623,1712 'axebuild':1372,1393 'background':384,480,505,1007 'baselin':106 'bash':1355 'beyond':1252 'bg':378,391 'blind':1608 'book':131 'bool':686 'bound':221 'box':52 'brand':122,130,475,1478 'break':909 'brows':1514 'browser':440,1450,1740 'bubbl':241,1226 'built':1815 'built-in':1814 'button':64,167,512,936,951,1263,1291,1531,1672,1674 'c':318,320,324,325,327,329,927,939,956,965 'c.name':928,940,966 'call':82,832,1245,1247,1257,1262,1277,1286,1328,1348,1510,1573,1692 'callback':888 'caption':1323,1332 'caus':1085 'chat':30,149,158,520,529,536,836,1253,1379,1428,1534,1560,1659 'chatcompon':654 'chatscreen':567,631 'check':289,1622 'checker':438 'checklist':1634 'checkmark':1598 'ci':294,408,1719 'clarifi':1298 'class':653 'clear':790,1587 'cleartimeout':586 'click':219,531 'clickabl':915,1485 'close':1050 'closeopenthread':1058 'cmd/ctrl':1037 'code':102 'color':39,116,123,260,272,278,375,385,1479,1590,1599,1607 'color-blind':1606 'color-on':1589 'cometchat':2,7,46,265,373,383,759,1730,1788,1805,1819 'cometchat-a11y':1 'cometchat-background-color':382 'cometchat-i18n':1729,1787 'cometchat-text-color':372 'cometchat.addmessagelistener':768,846 'cometchat.messagelistener':771,849 'cometchat.textmessage':774 'cometchat/chat-sdk-javascript':761 'cometchatmessagecompos':589,595,646 'common':473 'complet':1425 'compon':56,651,898,1014,1651,1811 'compos':552,578,658,659,709,979,985,1737 'composerfocus':698,712 'composerfocus.requestfocus':705 'composerref':569,591,633,648 'composerref.current':582,609,640 'composerview':693 'composerview.requestfocus':673 'composerview.sendaccessibilityevent':674 'consider':1251 'console.warn':395 'const':308,312,343,347,367,377,387,568,579,632,637,762,775,779,782,852,1033,1153,1162,1389 'consum':1728 'contentedit':612 'contentresolv':1201 'context':1214 'contrast':40,91,117,261,288,397,437,1476,1642 'contrast-check':436 'contrastratio':302,389 'control':548,1433,1657 'convers':217,533,971 'core':1363,1376,1625,1714 'correct':1804 'count':1135 'cover':22 'coverag':1613 'cross':1636 'cross-famili':1635 'css':268,286,365,986,1112,1113 'current':1726 'custom':81,93,119,468,905,1021,1124,1243,1472,1521,1638,1782,1807,1809 'dark':508 'dark-mod':507 'dart':695,1210 'decor':234 'deep':540 'default':463,904,1218,1780 'depend':1800 'design':432,1840 'dev':1360 'developer.apple.com':1849 'developer.apple.com/design/human-interface-guidelines/accessibility':1848 'devic':1416,1754 'disabl':1120,1517 'disableanim':1215 'discomfort':1087 'discover':1075 'disord':1094,1550 'disori':1553 'distract':1089 'div':924,943,949 'document':1066 'document.documentelement':370,380 'document.getelementbyid':784 'dom':455 'dot':239,1309,1706 'dp':1777 'durat':1128,1140,1203 'e':958,1035 'e.ctrlkey':1043 'e.key':960,962,1044,1056 'e.metakey':1042 'e.preventdefault':963,1046 'easi':1621 'effect':245 'elementref':660 'els':1189 'end':1261 'end-cal':1260 'ensur':792,1268 'enter':961 'entranc':242,1227 'entri':151,522,1259,1661 'equival':870 'escap':1049,1057 'etc':1760 'event':227 'everi':177,1432 'exit':1273 'expect':1368,1399 'explicit':945 'export':565,629,652 'extens':441 'f':1206,1208 'f0f0f0':487 'fade':1708 'fail':139,282,401,474,501,1481 'fals':1157 'famili':13,114,258,1637,1806,1820 'fg':368,390 'final':696,1191,1211 'findnodehandl':623,639 'finish':817 'first':791 'five':108 'flash':1305,1702 'flashes/sec':1318 'flow':1429 'flutter':21,426,694,865,880,1209 'focus':41,85,146,161,517,542,576,583,613,677,1002,1039,1048,1255,1526,1652,1761 'focusnod':697,699,711 'forbid':1314 'forward':598 'full':1427 'fun':670 'func':683 'function':301,566,630 'gap':73,109 'genuin':1570 'get':1552 'getcomputedstyl':369,379 'getnam':778,858 'getpropertyvalu':371,381 'gray':489,498 'guard':1233 'handl':638,642,644,1519 'handler':220,948,1034,1061,1065 'hangup':1266 'hear':204 'help':1074,1081 'hex':310 'hex.match':314 'hex1':303,346 'hex2':305,350 'hig':1846 'honor':1106,1680 'hook':1333 'html':750,981 'hz':1704 'i18n':1731,1789 'ignor':1543 'implement':655 'import':560,617,622,758,840,1131,1137,1143,1147,1366,1371 'in-app':1071 'includ':1016 'incom':831,1276,1285,1572,1691 'incoming-cal':1690 'index':997 'indic':238,1223,1307 'initst':702 'input':65,607,611 'inspector':1407 'instal':1357 'instead':223 'integr':10,47,112 'intern':1831 'interrupt':1562 'io':20,679,864,874,1179,1284,1404,1435,1774 'isreducemotionen':1679 'issu':1826 'item':207,916,1486 'iter':1134 'k':1038,1045 'keyboard':27,210,226,893,901,919,931,947,1026,1419,1487 'keyboard-access':900 'keyboard-on':209,1418 'keyboardev':1036 'keydown':1060,1064 'kit':9,55,61,80,461,593,896,1012,1121,1235,1445,1453,1518,1779,1810,1830 'kit-intern':1829 'know':192 'kotlin':667,668,1197 'l1':344,353,357 'l2':348,354,358 'label':1292,1576,1604,1684 'land':155,544,1653 'larg':1648 'latest':555 'left':992,1003 'less':1100 'let':1181 'light':488 'like':830 'link':541,968,980,989,1001,1018,1738 'list':67,176,202,218,739,972 'listen':752,1442 'listenerid':763,769,847 'live':34,181,714,748,810,824,1278,1331,1494,1557,1662,1695 'local':1727,1802 'long':970 'look':126,1828 'lumin':309,345,349 'm3.material.io':1843 'm3.material.io/foundations/accessible-design':1842 'ma':42 'maco':1441 'make':1448 'manag':86,147,518 'manual':171,427,1352,1417,1538,1627 'manual-test':1626 'map':317,323 'markup':71 'materi':1839 'math.max':352 'math.min':356 'matter':1076 'may':132 'meaning':547,1656 'media':1114 'mediaquery.of':1213 'meet':104 'messag':38,175,186,195,201,240,551,556,718,729,738,755,766,786,803,829,855,978,984,1225,1498,1503,1561,1668 'message-announc':785 'message-bubbl':1224 'microphon':1294,1583,1585 'mid':1566 'mid-sent':1565 'might':281 'minimum':144 'miss':1502 'modal':1054 'mode':509 'most':58 'motion':231,251,1079,1118,1232,1547,1678,1756 'mount':574 'mous':1424 'movement':1101 'ms':1130,1142 'msg':773,851 'msg.getsender':777,857 'msg.gettext':781,859 'must':1523,1784 'mute':497,504,1293,1297,1578,1582,1689 'mute/end':1290 'mute/end/camera':1683 'nativ':16,424,425,459,615,628,665,678,838,845,862,935,1145,1152,1178,1195,1402,1745 'native/flutter':274 'navig':28,84,172,199,215,526,735,894,1421,1541 'need':404,470,722,1237 'new':37,185,194,206,717,728,754,770,802,848,854,1392,1497,1667 'ngafterviewinit':661 'nice':127 'notif':690,876 'npm':1356 'null':571,635 'number':307 'nvda':1438 'observ':1758 'onclick':925,937,954 'one':256,299,1125 'one-shot':298 'onkeydown':957 'onresum':671 'ontextmessagereceiv':772,850 'open':534,1052,1533 'openconvers':926,938,955,964 'os':1108 'otherwis':731 'out-of-the-box':48 'output':1733 'overlay':1344 'overrid':275,669,682,700 'pad':1009 'page':1385,1394 'page.goto':1387 'palett':469 'parseint':319 'pass':466,483,492,1381,1640,1720,1781 'pattern':913,1470,1671 'per':1631 'physic':1086 'pick':124 'platform':867,1337,1698 'play':1507 'playwright':1715 'playwright/test':1370 'pointer':1786 'polit':811 'pop':1765 'posit':990 'prefer':249,1099,1109,1116,1545,1676,1757 'prefers-reduced-mot':248,1115,1544,1675 'present':1739 'preserv':1024,1812 'prevent':1320 'previous':165 'product':72,1029,1327 'pronunci':1796 'pt':1773 'pull':360 'purpl':476,485,494 'purpos':43 'push':1768 'px':994 'queri':604 'queryselector':610 'quick':1272 'ratio':388,393 'ratio.tofixed':398 're':742,797,1540 're-announc':796 're-navig':1539 're-read':741 'react':14,15,423,557,564,614,621,627,837,844,1144,1151 'react-nat':626,843,1150 'read':743,1593 'reader':32,153,188,720,934,1500,1795 'real':1415,1753 'receiv':1669 'recognit':1341 'record':1306,1326,1705 'reduc':230,250,1078,1117,1119,1231,1546,1677,1755 'reduce-mot':1230 'reduced-mot':229 'reducemot':1154,1175,1182,1185,1199,1212 'reducemotionchang':1165 'ref':590,599,647 'refer':1835 'reflow':1467 'region':35,182,715,749,783,789,1279,1495,1663,1696 'region.textcontent':799,801 'regress':1616 'releas':1632 'remain':1455 'report':1825 'requir':1105 'reserv':1568 'respect':247 'rest':1630 'restor':1762 'result':1390 'results.violations':1400 'return':333,351,585,588,645,1062,1167 'rgb':313,335,338,341 'right':929,942 'role':950 'run':292 'runtim':454 'save':1359 'save-dev':1358 'scale':1174,1204 'scanner':1411 'screen':31,88,150,159,187,521,530,719,933,1258,1349,1380,1499,1511,1660,1764,1794 'screen-read':932,1793 'screenchang':691 'scripts/check-contrast.ts':291 'sdk':887 'search':1040 'searchref.current':1047 'secondari':511 'see':233 'seizur':1321 'semant':70 'semanticsservice.announce':881 'sendernam':776,805 'sens':1449 'sentenc':1567 'set':1724,1803 'setreducemot':1155,1161,1166 'settimeout':581,662,800 'settings.global.animator':1202 'settings.global.getfloat':1200 'shape':885 'ship':68,450 'shortcut':1027,1068 'shot':300 'signal':1592 'silent':1480 'similar':1238 'simpli':1098 'singl':277 'sister':1790 'skill':100,253,416,1217,1732,1791 'skill-cometchat-a11y' 'skip':225,967,976,982,988,1000,1017,1575,1735 'skip-link':987,999 'skip-to-compos':1734 'skip-to-message-compos':975 'slip':1617 'smoke':1743,1749 'source-cometchat' 'speak':818 'specif':1249,1577,1686 'speech':1340 'speech-recognit':1339 'starter':419 'state':1192 'status':1594 'stay':162,1527 'string':304,306,311,1799 'strobe':1311,1710 'style':1172 'sub':1163 'sub.remove':1168 'subtl':514 'suit':414 'super.initstate':703 'super.onresume':672 'super.viewdidappear':687 'support':1324 'surfac':83,515 'swift':680,681,1180 'tab':537,1430 'tab/screen':152 'tabindex':952 'tag':1723 'talkback':1436,1747 'target':26,95,1770 'test':413,1350,1367,1378,1612,1628,1717,1744,1750 'tests/a11y/contrast.test.ts':422 'text':138,374,780,795,806,853,861,873,879,882,1345,1603,1645,1649 'text/background':396 'textdirection.ltr':883 'theme':94,120,263,266,431,447,464,1473,1639 'theme-design':430 'this.composer.nativeelement.focus':663 'thread':1053 'time':178,433 'timer':580,587 'toequal':1401 'toggl':510,1302 'token':273,448,465 'tool':439,1824 'top':1005 'topic-agent-skills' 'topic-ai-agent' 'topic-chat' 'topic-claude-code' 'topic-cometchat' 'topic-cursor' 'topic-messaging' 'topic-nextjs' 'topic-react' 'topic-react-native' 'topic-ui-kit' 'touch':1769 'transform':1173 'transit':89,244,1139 'transition-dur':1138 'translat':1798 'treatment':1239 'trigger':166,891,1530 'trim':376,386 'trip':111 'troubleshoot':1821 'ts':290,650,751,839,1031,1146,1365 'tsx':559,616,917 'type':237,1222 'typing-ind':236 'ui':8,54,1522,1650,1783 'uiaccessibility.isreducemotionenabled':1183,1759 'uiaccessibility.post':689,875,1282 'uiview.animate':1186 'unless':197 'unmut':1584 'unplug':1422 'urgent':828,1571 'usabl':1456 'use':434,821,1308,1707 'useeffect':561,572,619,636,1032,1158 'user':154,189,212,232,525,721,815,920,1083,1091,1270,1488,1501,1513,1535,1551,1564,1609 'useref':562,570,618,634 'usest':1156 'usual':549 'v5/v6':19 'v6':601 'val':1198 'valu':362 'variabl':269,287,366 've':1242 'verif':1633 'verifi':1446 'version':420 'vestibular':1093,1549 'vestibular-disord':1548 'via':608,1335 'video':1508 'view':676 'view.announceforaccessibility':872 'viewchild':657 'viewdidappear':684 'viewport':1464 'visibl':1602 'voiceov':1434,1440,1748 'void':701 'w':315 'w/g':316 'wait':812 'watch':502 'wcag':23,96,402,1102,1312,1465,1721,1832 'wcag2a':1396 'wcag2aa':1397 'web':284,558,745,1110,1353 'web/angular':1711 'web/rn':270,1280 'webaim.org':443 'webaim.org/resources/contrastchecker/)':442 'white':479,1008 'widget':710 'widgetsbinding.instance.addpostframecallback':704 'window':1439 'window.addeventlistener':1059 'window.removeeventlistener':1063 'wire':77 'withdur':1187 'without':1193,1474 'withtag':1395 'wrap':906,1022,1220 'write':101,417 'wrong':918 'www.w3.org':1837 'www.w3.org/wai/wcag21/quickref/?levels=aa':1836 'xcode':1405 'z':996 'z-index':995 'zoom':1451,1459,1741","prices":[{"id":"21c39b21-5dbe-4994-85d8-ad954ab72512","listingId":"12eb9d32-a03d-433c-84b2-82e6d7a334b2","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"cometchat","category":"cometchat-skills","install_from":"skills.sh"},"createdAt":"2026-05-18T07:04:15.674Z"}],"sources":[{"listingId":"12eb9d32-a03d-433c-84b2-82e6d7a334b2","source":"github","sourceId":"cometchat/cometchat-skills/cometchat-a11y","sourceUrl":"https://github.com/cometchat/cometchat-skills/tree/main/skills/cometchat-a11y","isPrimary":false,"firstSeenAt":"2026-05-18T07:04:15.674Z","lastSeenAt":"2026-05-18T19:04:44.433Z"}],"details":{"listingId":"12eb9d32-a03d-433c-84b2-82e6d7a334b2","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"cometchat","slug":"cometchat-a11y","github":{"repo":"cometchat/cometchat-skills","stars":27,"topics":["agent-skills","ai-agent","chat","claude-code","cometchat","cursor","messaging","nextjs","react","react-native","ui-kit"],"license":null,"html_url":"https://github.com/cometchat/cometchat-skills","pushed_at":"2026-05-18T05:04:24Z","description":"Add CometChat chat to any React, Next.js, React Native, Angular, Android, iOS, or Flutter project through your AI coding agent. Works with Claude Code, Cursor, Codex, VS Code Copilot, Windsurf, Cline, Kiro, and 50+ more agents.","skill_md_sha":"3d4fe46a8f75340dee3d5e25d2818fb2bd9086a7","skill_md_path":"skills/cometchat-a11y/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/cometchat/cometchat-skills/tree/main/skills/cometchat-a11y"},"layout":"multi","source":"github","category":"cometchat-skills","frontmatter":{"name":"cometchat-a11y","license":"MIT","description":"Accessibility (a11y) for CometChat UI Kit integrations across all families — React, React Native, Angular, Android (V5/V6), iOS, Flutter. Covers WCAG 2.1 AA targets, keyboard navigation in chat, screen reader announcements (live regions for new messages), color contrast, focus management on call screens, motion-reduction support, and the cross-family checks that catch the common production a11y bugs. Cross-family — applies wherever the agent is checking accessibility.","compatibility":"All CometChat UI Kit families v4.x / v5.x / v6.x"},"skills_sh_url":"https://skills.sh/cometchat/cometchat-skills/cometchat-a11y"},"updatedAt":"2026-05-18T19:04:44.433Z"}}