<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: babycat</title>
    <description>The latest articles on DEV Community by babycat (@babycat).</description>
    <link>https://dev.to/babycat</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4022684%2Fe78be405-e521-4234-81bb-9fcbc006d5d1.jpg</url>
      <title>DEV Community: babycat</title>
      <link>https://dev.to/babycat</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/babycat"/>
    <language>en</language>
    <item>
      <title>"Show Agent Confidence Without Making It Look Like Permission"</title>
      <dc:creator>babycat</dc:creator>
      <pubDate>Wed, 29 Jul 2026 09:47:16 +0000</pubDate>
      <link>https://dev.to/babycat/show-agent-confidence-without-making-it-look-like-permission-4o6b</link>
      <guid>https://dev.to/babycat/show-agent-confidence-without-making-it-look-like-permission-4o6b</guid>
      <description>&lt;p&gt;A green “95% confident” badge beside an action button can read like approval even when the user lacks permission. Color and proximity accidentally merge two different questions: how certain is the suggestion, and who may authorize it?&lt;/p&gt;

&lt;h2&gt;
  
  
  What the announcement establishes
&lt;/h2&gt;

&lt;p&gt;GitHub introduced agent automation controls in GitHub Issues as a public preview on July 23, 2026. The announcement discusses confidence and rationale signals. Because it is a preview, interfaces may change; this example is a general UI pattern, not GitHub UI code. &lt;a href="https://github.blog/changelog/2026-07-23-agent-automation-controls-in-github-issues-in-public-preview/" rel="noopener noreferrer"&gt;Read the primary source&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For test track 1.1, the engineering claim here is narrower than the announcement: the surrounding workflow needs a contract that remains valid when metadata, transport, people, or executors change.&lt;/p&gt;

&lt;h2&gt;
  
  
  The artifact
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;article&lt;/span&gt; &lt;span class="na"&gt;aria-labelledby=&lt;/span&gt;&lt;span class="s"&gt;"agent-summary"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"agent-summary"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Proposed issue update&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;&lt;/span&gt;Confidence&lt;span class="nt"&gt;&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;&lt;/span&gt;High (model estimate)&lt;span class="nt"&gt;&amp;lt;/dd&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;dt&amp;gt;&lt;/span&gt;Rationale&lt;span class="nt"&gt;&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;&lt;/span&gt;Matches the reproduction steps&lt;span class="nt"&gt;&amp;lt;/dd&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;dt&amp;gt;&lt;/span&gt;Authorization&lt;span class="nt"&gt;&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;&lt;/span&gt;Not granted&lt;span class="nt"&gt;&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"status"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Review required; no change has been applied.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Apply update&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/article&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For test track 1.2, this is a design fixture, not executed code. Pin language and dependency versions before turning it into a repository test, and replace example identities and timestamps with disposable values.&lt;/p&gt;

&lt;h2&gt;
  
  
  Success path
&lt;/h2&gt;

&lt;p&gt;After a fresh permission check, enable the action and announce that authorization was verified. Keep rationale available as text, preserve heading order, and provide no color-only distinctions.&lt;/p&gt;

&lt;p&gt;For test track 1.3, a successful demonstration records inputs, policy or schema version, decision, and final identifier. It does not infer correctness from a confidence label, status badge, or fluent output.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure path and regression plan
&lt;/h2&gt;

&lt;p&gt;If permission expires or issue content changes, disable apply, mark the analysis stale, move no focus automatically, and offer a clearly named refresh action. Never relabel confidence as “safe.”&lt;/p&gt;

&lt;p&gt;Check keyboard order, zoom, forced colors, reduced motion, screen-reader reading order, missing rationale, unknown confidence, revoked permission, stale issue revision, failed refresh, and double activation.&lt;/p&gt;

&lt;p&gt;For test track 1.4, the acceptance gate is binary: the negative fixture must produce no unauthorized or duplicate side effect, while the positive fixture must remain traceable to its initial evidence. Expected output should be documented before execution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cleanup and rollback
&lt;/h2&gt;

&lt;p&gt;Remove the enhanced card behind its feature flag and return to the existing review flow. Do not hide an already-applied outcome; retain an accessible audit message and offer the normal undo mechanism.&lt;/p&gt;

&lt;p&gt;For test track 1.5, cleanup must preserve enough sanitized evidence to distinguish cancellation, rejection, stale work, and successful completion. Never solve recovery by silently marking an uncertain operation successful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;p&gt;For test track 1.6, this article proposes a compact engineering exercise and reports no execution results. It does not evaluate service availability, security, accessibility conformance, productivity, or comparative quality. Product previews can change, and a local fixture cannot reproduce every hosted-system failure.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical development environment
&lt;/h2&gt;

&lt;p&gt;For building and reviewing a separate prototype, MonkeyCode is an open-source AGPL-3.0 AI development platform with an overseas hosted option. It includes a managed server-side cloud development environment, integrated models, task and requirement management, and build, test, and preview workflows. It is free to start. These statements do not mean the GitHub or OpenAI capability discussed above exists in MonkeyCode. Check the console for current quotas, models, regions, duration, and pricing before planning work. &lt;a href="https://ly.cyberserval.tech/iIETXiF" rel="noopener noreferrer"&gt;Open the campaign workspace&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Disclosure: This article promotes MonkeyCode using an official campaign link. I’m a MonkeyCode user, not affiliated with the project, and I receive no commission from this link.&lt;/p&gt;

&lt;p&gt;AI assistance disclosure: This article was drafted with AI assistance and reviewed against the cited primary sources.&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>html</category>
      <category>github</category>
      <category>ux</category>
    </item>
    <item>
      <title>"Build an Accessible Navigator for Build, Test, and Preview Results"</title>
      <dc:creator>babycat</dc:creator>
      <pubDate>Tue, 28 Jul 2026 03:41:22 +0000</pubDate>
      <link>https://dev.to/babycat/build-an-accessible-navigator-for-build-test-and-preview-results-1mkb</link>
      <guid>https://dev.to/babycat/build-an-accessible-navigator-for-build-test-and-preview-results-1mkb</guid>
      <description>&lt;p&gt;Build, test, and preview produce a small forest of results. Turning every update into a toast makes the forest louder, not clearer. A result navigator should let keyboard and screen-reader users answer three questions: what finished, what failed, and where can I inspect the evidence?&lt;/p&gt;

&lt;p&gt;Start with native landmarks and links. The following is a minimal static structure; dynamic code must update the text without replacing the focused element.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;nav&lt;/span&gt; &lt;span class="na"&gt;aria-labelledby=&lt;/span&gt;&lt;span class="s"&gt;"results-heading"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"results-heading"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Run results&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#build-result"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Build &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;passed&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#test-result"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Tests &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;2 failed&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#preview-result"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Preview &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;not created&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"run-announcement"&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"status"&lt;/span&gt; &lt;span class="na"&gt;aria-atomic=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Tests completed: 2 failed.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;main&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"build-result"&lt;/span&gt; &lt;span class="na"&gt;tabindex=&lt;/span&gt;&lt;span class="s"&gt;"-1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Build&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&amp;lt;pre&amp;gt;&lt;/span&gt;exit 0&lt;span class="nt"&gt;&amp;lt;/pre&amp;gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"test-result"&lt;/span&gt; &lt;span class="na"&gt;tabindex=&lt;/span&gt;&lt;span class="s"&gt;"-1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Tests&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#failure-1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Jump to first failure&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;article&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"failure-1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;h3&amp;gt;&lt;/span&gt;normalizes blank input&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&amp;lt;pre&amp;gt;&lt;/span&gt;expected ...&lt;span class="nt"&gt;&amp;lt;/pre&amp;gt;&amp;lt;/article&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"preview-result"&lt;/span&gt; &lt;span class="na"&gt;tabindex=&lt;/span&gt;&lt;span class="s"&gt;"-1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Preview&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;&lt;/span&gt;Unavailable because tests failed.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;role="status"&lt;/code&gt; for a concise completion announcement, not streaming logs. If the user activates “Jump to first failure,” move programmatic focus to the target heading or section and preserve a visible focus indicator. Do not move focus merely because background work completes.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Keyboard expectation&lt;/th&gt;
&lt;th&gt;Announcement&lt;/th&gt;
&lt;th&gt;Visual expectation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;run starts&lt;/td&gt;
&lt;td&gt;focus stays on trigger&lt;/td&gt;
&lt;td&gt;“Run started” once&lt;/td&gt;
&lt;td&gt;all stages labeled pending&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;build passes&lt;/td&gt;
&lt;td&gt;no focus move&lt;/td&gt;
&lt;td&gt;no noisy intermediate speech&lt;/td&gt;
&lt;td&gt;pass text, not color alone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;tests fail&lt;/td&gt;
&lt;td&gt;navigator link works&lt;/td&gt;
&lt;td&gt;failure count once&lt;/td&gt;
&lt;td&gt;error summary before details&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;preview ready&lt;/td&gt;
&lt;td&gt;link enters tab order&lt;/td&gt;
&lt;td&gt;ready + expiry summary&lt;/td&gt;
&lt;td&gt;commit/version visible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;preview expires&lt;/td&gt;
&lt;td&gt;stale link disabled safely&lt;/td&gt;
&lt;td&gt;expiry once if relevant&lt;/td&gt;
&lt;td&gt;reason and rebuild action&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;retry begins&lt;/td&gt;
&lt;td&gt;focus stays on retry&lt;/td&gt;
&lt;td&gt;new run identity&lt;/td&gt;
&lt;td&gt;old evidence remains labeled&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Test success with Tab, Shift+Tab, Enter, browser zoom, forced colors, and at least one screen-reader/browser pairing that your team supports. Failure includes a keyboard trap in logs, status conveyed only by icon/color, repeated live-region chatter, focus jumping on completion, or a preview link whose version is unclear.&lt;/p&gt;

&lt;p&gt;On failure, keep evidence readable, disable unsafe preview actions, return focus to the error summary only after an explicit user command, and offer retry without erasing the previous run. On exit, cancel polling, revoke preview access, delete the temporary workspace, and confirm the UI no longer presents a live URL.&lt;/p&gt;

&lt;p&gt;This snippet does not establish accessibility conformance. &lt;code&gt;pre&lt;/code&gt; content may need wrapping and copy controls, large logs need virtualization that preserves reading order, and announcements require testing with real assistive technology. The code and matrix are unexecuted artifacts, not claimed user research.&lt;/p&gt;

&lt;h2&gt;
  
  
  Put the navigator around a bounded trial
&lt;/h2&gt;

&lt;p&gt;Current official MonkeyCode material says the overseas hosted option is “Free to start” and describes integrated models with managed server-side cloud development environments that support build, test, and preview. That gives the navigator realistic stages without implying that every model or server is free. Exact availability, quotas, regions, duration, later pricing, and terms can change and must be checked in the current console.&lt;/p&gt;

&lt;p&gt;When the semantic prototype is ready, its official campaign starting point is &lt;a href="https://ly.cyberserval.tech/iIETXiF" rel="noopener noreferrer"&gt;https://ly.cyberserval.tech/iIETXiF&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let users end deliberately: stop updates, export accessible evidence, revoke preview access, delete the temporary workspace, and announce confirmed completion without stealing focus. If cleanup cannot be confirmed, leave a visible unresolved status instead of a false success.&lt;/p&gt;

&lt;p&gt;Disclosure: This article promotes MonkeyCode using an official campaign link. I’m a MonkeyCode user, not affiliated with the project, and I receive no commission from this link.&lt;/p&gt;

&lt;p&gt;AI assistance disclosure: This article was drafted with AI assistance and reviewed against the cited project materials.&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>frontend</category>
      <category>html</category>
      <category>testing</category>
    </item>
    <item>
      <title>"Build an Accessible Findings Queue That Shows Its Evidence Type"</title>
      <dc:creator>babycat</dc:creator>
      <pubDate>Mon, 27 Jul 2026 13:38:41 +0000</pubDate>
      <link>https://dev.to/babycat/build-an-accessible-findings-queue-that-shows-its-evidence-type-j25</link>
      <guid>https://dev.to/babycat/build-an-accessible-findings-queue-that-shows-its-evidence-type-j25</guid>
      <description>&lt;p&gt;A keyboard user opens a finding and hears only “High severity issue.” They cannot tell whether it came from a repeatable CodeQL query or AI-assisted detection, and the proposed Autofix looks like an already approved change. The missing UI state is provenance plus review status.&lt;/p&gt;

&lt;p&gt;GitHub made Code Quality generally available on July 20 for Enterprise Cloud and Team. Its &lt;a href="https://github.blog/changelog/2026-07-20-github-code-quality-is-now-generally-available/" rel="noopener noreferrer"&gt;latest verified official signal&lt;/a&gt; describes deterministic CodeQL, AI-assisted detection, and reviewable Autofix. GitHub's internal resolution metric is GitHub-reported, not an accessibility or universal-quality benchmark. I found no newer reliable official July 27 release and rejected unverified secondary claims.&lt;/p&gt;

&lt;h2&gt;
  
  
  Model evidence separately from workflow
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Finding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;deterministic&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ai_assisted&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nl"&gt;evidence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;fix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;patch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;proposed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;accepted&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rejected&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;origin&lt;/code&gt; says how a finding was produced; it does not say whether it is correct. &lt;code&gt;fix.status&lt;/code&gt; records a human workflow decision; it does not rewrite provenance. Keep these dimensions independent.&lt;/p&gt;

&lt;h2&gt;
  
  
  A small semantic card
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;article&lt;/span&gt; &lt;span class="na"&gt;aria-labelledby=&lt;/span&gt;&lt;span class="s"&gt;"f-17-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h3&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"f-17-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Unsanitized redirect target&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;&lt;/span&gt;Detection:&lt;span class="nt"&gt;&amp;lt;/strong&amp;gt;&lt;/span&gt; Deterministic CodeQL rule&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;&lt;/span&gt;Location:&lt;span class="nt"&gt;&amp;lt;/strong&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;code&amp;gt;&lt;/span&gt;routes.ts:44&lt;span class="nt"&gt;&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;details&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;summary&amp;gt;&lt;/span&gt;Review evidence and proposed fix&lt;span class="nt"&gt;&amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"f-17-evidence"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Source reaches a redirect sink.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;pre&lt;/span&gt; &lt;span class="na"&gt;tabindex=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Proposed patch"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/pre&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/details&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;fieldset&lt;/span&gt; &lt;span class="na"&gt;aria-describedby=&lt;/span&gt;&lt;span class="s"&gt;"f-17-evidence"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;legend&amp;gt;&lt;/span&gt;Autofix decision&lt;span class="nt"&gt;&amp;lt;/legend&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;label&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"f-17"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"accept"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; Accept after review&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;label&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"f-17"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"reject"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; Reject with reason&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/fieldset&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Save decision&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/article&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use visible words, not color or icons alone. “AI-assisted finding” is clearer than a sparkle. “Proposed Autofix” is clearer than “Fixed.” A focusable &lt;code&gt;pre&lt;/code&gt; lets keyboard users scroll long patches, but a real implementation also needs line-by-line additions/deletions and a plain-text fallback.&lt;/p&gt;

&lt;h2&gt;
  
  
  State behavior
&lt;/h2&gt;

&lt;p&gt;Announce counts politely, saving briefly, failures assertively, and resolution without reading the whole patch. Keep focus on the triggering control after expansion, move it to an error summary after failure, and send it to the next card when one disappears.&lt;/p&gt;

&lt;p&gt;Normal path: the reviewer filters by origin, opens evidence, inspects the patch, selects a decision, and receives a durable confirmation without focus moving unexpectedly.&lt;/p&gt;

&lt;p&gt;Failure path: saving returns an error or the finding revision changed. Preserve the choice locally, mark the record stale, focus a linked error summary, and require the new patch to be reviewed. Never submit the old acceptance against a changed Autofix.&lt;/p&gt;

&lt;p&gt;A revision-aware request might be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"findingId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"f-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"findingRevision"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"decision"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"accept"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reviewedPatchHash"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sha256:..."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server rejects mismatched revisions with &lt;code&gt;409 Conflict&lt;/code&gt;. The interface then says “The proposed fix changed; review it again,” expands the new evidence, and leaves the decision unset.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessibility QA
&lt;/h2&gt;

&lt;p&gt;Test keyboard order and traps, screen-reader labels, 400% reflow, reduced motion, and high-contrast status cues. Record browser, OS, assistive technology, and versions; this proposed matrix was not executed.&lt;/p&gt;

&lt;p&gt;Release checklist:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Expose deterministic versus AI-assisted origin in text.&lt;/li&gt;
&lt;li&gt;Keep severity, confidence, provenance, and decision as separate fields.&lt;/li&gt;
&lt;li&gt;Require explicit review before an Autofix is accepted.&lt;/li&gt;
&lt;li&gt;Bind decisions to a patch hash or finding revision.&lt;/li&gt;
&lt;li&gt;Support reject-with-reason and retry after save failure.&lt;/li&gt;
&lt;li&gt;Preserve focus when sorting or removing cards.&lt;/li&gt;
&lt;li&gt;Check current plan availability and product behavior.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Limits: this component does not establish CodeQL semantics, finding correctness, WCAG conformance, or GitHub UI behavior. A card view may be insufficient for very large patches; provide a full diff. Product labels and availability can change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Evaluate the same UI discipline elsewhere
&lt;/h2&gt;

&lt;p&gt;MonkeyCode can be evaluated after these states are defined. Verified material describes an open-source AGPL-3.0 AI development platform, overseas online option, managed server-side cloud development environments, model/task/requirement management, and build/test/preview, free to start. I have not tested its interface. If it fits your candidate list, use the &lt;a href="https://ly.cyberserval.tech/iIETXiF" rel="noopener noreferrer"&gt;official campaign route&lt;/a&gt; and repeat the keyboard, stale-revision, and failure review rather than assuming accessibility.&lt;/p&gt;

&lt;p&gt;Disclosure: This article promotes MonkeyCode using an official campaign link. I’m a MonkeyCode user, not affiliated with the project, and I receive no commission from this link.&lt;/p&gt;

&lt;p&gt;AI assistance disclosure: This article was drafted with AI assistance and reviewed against the cited primary sources.&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>frontend</category>
      <category>github</category>
      <category>ai</category>
    </item>
    <item>
      <title>"Evaluate MonkeyCode Task Status as an Accessible User Flow"</title>
      <dc:creator>babycat</dc:creator>
      <pubDate>Mon, 27 Jul 2026 04:15:38 +0000</pubDate>
      <link>https://dev.to/babycat/evaluate-monkeycode-task-status-as-an-accessible-user-flow-kln</link>
      <guid>https://dev.to/babycat/evaluate-monkeycode-task-status-as-an-accessible-user-flow-kln</guid>
      <description>&lt;p&gt;If a task becomes “needs approval” but only badge color changes, keyboard and screen-reader users may miss the decision. This promotional MonkeyCode framework proposes a test; it does not claim the current interface has that defect.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;State&lt;/th&gt;
&lt;th&gt;Visible text&lt;/th&gt;
&lt;th&gt;Announcement&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;planned&lt;/td&gt;
&lt;td&gt;scope ready&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;review&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;queued&lt;/td&gt;
&lt;td&gt;waiting&lt;/td&gt;
&lt;td&gt;polite once&lt;/td&gt;
&lt;td&gt;cancel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;running&lt;/td&gt;
&lt;td&gt;in progress&lt;/td&gt;
&lt;td&gt;milestones only&lt;/td&gt;
&lt;td&gt;inspect&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;approval&lt;/td&gt;
&lt;td&gt;decision required&lt;/td&gt;
&lt;td&gt;assertive once&lt;/td&gt;
&lt;td&gt;approve/reject&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;failed&lt;/td&gt;
&lt;td&gt;stopped with reason&lt;/td&gt;
&lt;td&gt;assertive once&lt;/td&gt;
&lt;td&gt;retry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;complete&lt;/td&gt;
&lt;td&gt;evidence ready&lt;/td&gt;
&lt;td&gt;polite once&lt;/td&gt;
&lt;td&gt;review diff&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Unexecuted example, not copied from MonkeyCode --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;aria-labelledby=&lt;/span&gt;&lt;span class="s"&gt;"task-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"task-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Update greeting validation&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
 &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"status"&lt;/span&gt; &lt;span class="na"&gt;aria-live=&lt;/span&gt;&lt;span class="s"&gt;"polite"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Waiting to begin&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
 &lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt;Evidence&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
 &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&amp;lt;li&amp;gt;&lt;/span&gt;Base commit: 31a…&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&lt;/span&gt;Allowed file: greet.py&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
 &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Cancel task&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do not announce every log line. When asynchronous approval appears, announce it and expose a predictable heading; unexpected focus movement interrupts reading. Approval and rejection need separate text labels rather than color-only icons.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;At 200% zoom, reach every action with Tab and Shift+Tab.&lt;/li&gt;
&lt;li&gt;Keep focus elsewhere while queued status updates; it must not be stolen.&lt;/li&gt;
&lt;li&gt;Verify approval scope and expiry are understandable.&lt;/li&gt;
&lt;li&gt;Cancel by keyboard and identify what was applied.&lt;/li&gt;
&lt;li&gt;Trigger a synthetic failure and recover without parsing raw logs.&lt;/li&gt;
&lt;li&gt;Reload; old status must not masquerade as a fresh event.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Record browser, OS, assistive technology/version, viewport, transition, expectation, observation, and severity. Stop approval for silent critical state, trapped focus, color-only scope, or ambiguous cancellation.&lt;/p&gt;

&lt;p&gt;Add two interruption cases to the pass. First, open the approval heading through a landmark list, activate rejection, and confirm focus lands on a text explanation rather than disappearing into the page root. Second, increase text spacing to 1.5 times line height and 0.12 times letter spacing; controls must remain named, visible, and operable without horizontal scrolling at a narrow viewport. For timeout recovery, let the session expire while the diff is open: returning should identify the expired context and require fresh authentication without silently retaining an actionable approval. Record the accessible name and focus target before and after each transition.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verified product boundary
&lt;/h2&gt;

&lt;p&gt;Current official material presents MonkeyCode as an open-source AGPL-3.0 AI development platform. For overseas online use, managed server-side cloud environments accompany the built-in development environment, model/task/requirement management, build, test, and preview.&lt;/p&gt;

&lt;h2&gt;
  
  
  Review record fields
&lt;/h2&gt;

&lt;p&gt;For review pass 1 in this babycat evaluation, record an owner, repository, base commit, requirement revision, allowed paths, start and stop times, expected checks, observed terminal state, reviewer decision, cleanup proof, and unresolved questions. Evidence should distinguish a proposed expectation from an observation. Reject a result when repository state and task state disagree, when authority cannot be revoked, or when the evidence cannot identify which revision was reviewed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;p&gt;This babycat method was not executed against a live MonkeyCode environment. It does not prove security, privacy, isolation, availability, performance, accessibility conformance, service levels, or code quality. Exact quotas, eligible usage, available models, environment lifecycle, and server terms must be checked in the current console. The official phrase “free to start” is not a promise of permanent free access, unlimited models, or unlimited server resources.&lt;/p&gt;

&lt;p&gt;Supporting official project material is at &lt;a href="https://github.com/chaitin/MonkeyCode" rel="noopener noreferrer"&gt;https://github.com/chaitin/MonkeyCode&lt;/a&gt;. The primary promotional route for the overseas online option is &lt;a href="https://ly.cyberserval.tech/iIETXiF" rel="noopener noreferrer"&gt;https://ly.cyberserval.tech/iIETXiF&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Disclosure: This article promotes MonkeyCode using an official campaign link. I’m a MonkeyCode user, not affiliated with the project, and I receive no commission from this link.&lt;/p&gt;

&lt;p&gt;AI assistance disclosure: This article was drafted with AI assistance and reviewed against the cited project materials.&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>frontend</category>
      <category>ai</category>
      <category>ux</category>
    </item>
    <item>
      <title>Design an Accessible Degraded Mode for AI Errors</title>
      <dc:creator>babycat</dc:creator>
      <pubDate>Sat, 25 Jul 2026 11:57:32 +0000</pubDate>
      <link>https://dev.to/babycat/design-an-accessible-degraded-mode-for-ai-errors-49cg</link>
      <guid>https://dev.to/babycat/design-an-accessible-degraded-mode-for-ai-errors-49cg</guid>
      <description>&lt;p&gt;A model request fails, a red toast disappears, and keyboard focus remains on a disabled Send button. The interface has technically reported an error while giving the user no understandable next action.&lt;/p&gt;

&lt;p&gt;Provider status should inform this design without becoming UI certainty. OpenAI records a &lt;a href="https://status.openai.com/incidents/01KYC921K145JTR1JK7DYKGWH1" rel="noopener noreferrer"&gt;July 25 incident&lt;/a&gt; starting at 09:17:49 UTC, mitigation monitoring at 10:02:52, and resolution at 11:08:36. A &lt;a href="https://status.openai.com/incidents/01KYCGY017EG43XZS6GFVXA8VH" rel="noopener noreferrer"&gt;new incident&lt;/a&gt; started at 11:35:24; at research time, it was identified with elevated errors and mitigation in progress. The &lt;a href="https://status.openai.com/" rel="noopener noreferrer"&gt;official page&lt;/a&gt; showed Partial System Degradation. That snapshot cannot justify claims about cause, global impact, user totals, or the later incident's final recovery.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give degradation its own state
&lt;/h2&gt;

&lt;p&gt;Do not overload &lt;code&gt;loading&lt;/code&gt; and &lt;code&gt;error&lt;/code&gt;. This unexecuted TypeScript model keeps the user's draft separate from remote status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;RequestState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;editing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;draft&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sending&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;draft&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;attemptId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;outcome_unknown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;draft&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;attemptId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;degraded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;draft&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;local&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;provider_signal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ready_to_switch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;draft&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;reviewed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An unknown outcome is not a failed outcome. Preserve the editable text, request identifier, attachments list, and timestamp locally according to the product's privacy policy. Never clear the composer on submission alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Semantic structure before decoration
&lt;/h2&gt;

&lt;p&gt;This markup is a proposed pattern, not a tested accessibility result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;aria-labelledby=&lt;/span&gt;&lt;span class="s"&gt;"service-state-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"service-state-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;AI service is degraded&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"service-state-detail"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Your draft is saved on this device. The previous request may still complete.
  &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Check this request&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Review another provider&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"status"&lt;/span&gt; &lt;span class="na"&gt;aria-live=&lt;/span&gt;&lt;span class="s"&gt;"polite"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"request-updates"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Move focus only when the user's current control disappears or continuing would be unsafe. A background status refresh belongs in the polite live region; repeating the entire incident message every poll creates noise. Keep the status heading in document order so screen-reader and magnification users can find it without interruption.&lt;/p&gt;

&lt;p&gt;The fallback control should say “Review another provider,” not “Continue safely.” Multi-provider fallback has semantic risks: models may change meaning, tool behavior, formatting, or safety behavior. Before switching, show what data will be resent, whether context transfers, what capabilities differ, and whether the old attempt could still finish.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Transition&lt;/th&gt;
&lt;th&gt;Visual&lt;/th&gt;
&lt;th&gt;Announcement&lt;/th&gt;
&lt;th&gt;Keyboard action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;sending → unknown&lt;/td&gt;
&lt;td&gt;persistent inline panel&lt;/td&gt;
&lt;td&gt;outcome is unknown&lt;/td&gt;
&lt;td&gt;Check request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;unknown → degraded&lt;/td&gt;
&lt;td&gt;status badge plus details&lt;/td&gt;
&lt;td&gt;service signal changed&lt;/td&gt;
&lt;td&gt;keep current focus&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;degraded → review&lt;/td&gt;
&lt;td&gt;comparison disclosure&lt;/td&gt;
&lt;td&gt;provider choice opened&lt;/td&gt;
&lt;td&gt;focus dialog heading&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;review → editing&lt;/td&gt;
&lt;td&gt;preserved draft&lt;/td&gt;
&lt;td&gt;no request sent&lt;/td&gt;
&lt;td&gt;return to trigger&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A QA pass should cover keyboard-only navigation, zoom, reduced motion, forced colors, and at least one screen-reader/browser pair with versions recorded. Those are proposed checks; no conformance claim or assistive-technology test was performed here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Offer choices without overselling them
&lt;/h2&gt;

&lt;p&gt;For developers seeking another environment, the overseas &lt;a href="https://monkeycode-ai.net/" rel="noopener noreferrer"&gt;MonkeyCode hosted page&lt;/a&gt; currently says “Start free.” Its official README describes managed server-side cloud environments offering build, test, and preview with integrated models. Present it as free to start, with current model/server quotas, regions, and uptime or SLA terms checked in the console rather than frozen into interface copy.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/chaitin/MonkeyCode" rel="noopener noreferrer"&gt;official MonkeyCode GitHub repository&lt;/a&gt; is AGPL-3.0 open source. The README at reviewed main commit &lt;code&gt;18baaf54937a65a7d47f1f9d83dd808777aa6cea&lt;/code&gt; describes built-in development environment, model, task, and requirement management. In an exit panel, I would distinguish “try hosted” from “inspect or self-host source.” Neither option guarantees outage protection, and hosted MonkeyCode reliability was not tested.&lt;/p&gt;

&lt;p&gt;This recommendation belongs after preserving the user's work and explaining uncertainty. A provider link is not a recovery experience; retained drafts, honest states, and pointer-independent controls are.&lt;/p&gt;

&lt;p&gt;Disclosure: I'm a MonkeyCode user sharing my own experience, not affiliated with the project.&lt;/p&gt;

&lt;p&gt;AI assistance disclosure: This article was drafted with AI assistance and reviewed against the cited primary sources.&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>frontend</category>
      <category>ai</category>
      <category>ux</category>
    </item>
    <item>
      <title>Design a Health Dashboard That Exposes Uncertainty and Connection Errors</title>
      <dc:creator>babycat</dc:creator>
      <pubDate>Sat, 25 Jul 2026 11:03:37 +0000</pubDate>
      <link>https://dev.to/babycat/design-a-health-dashboard-that-exposes-uncertainty-and-connection-errors-1412</link>
      <guid>https://dev.to/babycat/design-a-health-dashboard-that-exposes-uncertainty-and-connection-errors-1412</guid>
      <description>&lt;p&gt;A lab card says “No data,” but that phrase could mean no matching record, a disconnected source, a delayed refresh, a filter mismatch, or a request failure. Collapsing those states into an empty chart makes uncertainty look like certainty. The interface needs two layers: what the source reports and what the application currently knows about that report.&lt;/p&gt;

&lt;p&gt;OpenAI’s July 23, 2026 announcement says Health in ChatGPT is rolling out on web and iOS to eligible logged-in US users age 18 and older. According to &lt;a href="https://openai.com/index/health-in-chatgpt/" rel="noopener noreferrer"&gt;the announcement&lt;/a&gt;, supported connections include medical records and Apple Health, while the dashboard can cover labs, medications, activity, sleep, and other health information. OpenAI states connected data and relevant conversations are not used to train foundation models or target ads. This article treats those as attributed statements and proposes a generic UI pattern, not a reconstruction of that interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give each card an evidence state
&lt;/h2&gt;

&lt;p&gt;Do not make color carry the entire message. A card can use this &lt;strong&gt;unexecuted TypeScript model&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;SourceState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;current&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;observedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;sourceLabel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stale&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;lastSuccessAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;sourceLabel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;disconnected&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;sourceLabel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;partial&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;available&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;retryable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;MeaningState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reported&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;missing&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;none_found&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;filtered&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;unknown&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;unsupported&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;explanation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;HealthCard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SourceState&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;meaning&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MeaningState&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;SourceState&lt;/code&gt; answers whether retrieval is trustworthy enough to display. &lt;code&gt;MeaningState&lt;/code&gt; answers what can honestly be said about the value. Keeping them separate prevents a successful network response from implying interpretation and prevents an error from being shown as a zero.&lt;/p&gt;

&lt;h2&gt;
  
  
  Semantic card structure
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;aria-labelledby=&lt;/span&gt;&lt;span class="s"&gt;"sleep-title"&lt;/span&gt; &lt;span class="na"&gt;aria-describedby=&lt;/span&gt;&lt;span class="s"&gt;"sleep-status"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"sleep-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Sleep&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"sleep-status"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Last updated July 22 at 8:10 AM from connected source.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;&lt;/span&gt;395 minutes&lt;span class="nt"&gt;&amp;lt;/strong&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;reported duration&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;details&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;summary&amp;gt;&lt;/span&gt;What this card can and cannot tell you&lt;span class="nt"&gt;&amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;This shows received data. It does not interpret its medical meaning.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/details&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Refresh&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"status"&lt;/span&gt; &lt;span class="na"&gt;aria-live=&lt;/span&gt;&lt;span class="s"&gt;"polite"&lt;/span&gt; &lt;span class="na"&gt;aria-atomic=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use the live region only for the result of a user-triggered refresh, not every background synchronization. Keep focus on the refresh button after success. On a nonretryable connection failure, move neither focus nor the user into a dead end: expose “Review connection” beside a plain-language explanation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Content matrix
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;State&lt;/th&gt;
&lt;th&gt;Heading text&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Announcement&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;stale&lt;/td&gt;
&lt;td&gt;“Last updated…”&lt;/td&gt;
&lt;td&gt;Refresh&lt;/td&gt;
&lt;td&gt;only after requested refresh&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;disconnected&lt;/td&gt;
&lt;td&gt;“Source disconnected”&lt;/td&gt;
&lt;td&gt;Review connection&lt;/td&gt;
&lt;td&gt;announce transition once&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;partial&lt;/td&gt;
&lt;td&gt;“Some records unavailable”&lt;/td&gt;
&lt;td&gt;Show included range&lt;/td&gt;
&lt;td&gt;do not announce counts repeatedly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;error&lt;/td&gt;
&lt;td&gt;“Could not update”&lt;/td&gt;
&lt;td&gt;Retry when safe&lt;/td&gt;
&lt;td&gt;include retry result&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;missing&lt;/td&gt;
&lt;td&gt;“No matching record found”&lt;/td&gt;
&lt;td&gt;Review filters&lt;/td&gt;
&lt;td&gt;distinguish from zero&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A skeleton loader should reserve layout but must not expose fake values to assistive technology. Disable neither navigation nor disconnect controls while one card refreshes. If several cards fail together, provide a page-level summary with links to affected headings rather than repeating identical alerts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keyboard and assistive-technology QA
&lt;/h2&gt;

&lt;p&gt;This is a proposed matrix, not executed results:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Transition&lt;/th&gt;
&lt;th&gt;Keyboard check&lt;/th&gt;
&lt;th&gt;Screen-reader check&lt;/th&gt;
&lt;th&gt;Visual check&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;current to stale&lt;/td&gt;
&lt;td&gt;action remains reachable&lt;/td&gt;
&lt;td&gt;freshness text is associated&lt;/td&gt;
&lt;td&gt;icon plus text changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;refresh to error&lt;/td&gt;
&lt;td&gt;focus stays stable&lt;/td&gt;
&lt;td&gt;one concise status message&lt;/td&gt;
&lt;td&gt;error is near card&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;connected to disconnected&lt;/td&gt;
&lt;td&gt;disconnect remains reversible&lt;/td&gt;
&lt;td&gt;source name is announced&lt;/td&gt;
&lt;td&gt;no value shown as current&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;partial to current&lt;/td&gt;
&lt;td&gt;details state persists&lt;/td&gt;
&lt;td&gt;heading structure unchanged&lt;/td&gt;
&lt;td&gt;chart does not jump&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Record browser, OS, zoom, contrast mode, and assistive-technology versions when running it. Test at 200% zoom and with CSS disabled to confirm source, freshness, value, and action remain in a sensible reading order.&lt;/p&gt;

&lt;h2&gt;
  
  
  Boundaries
&lt;/h2&gt;

&lt;p&gt;This pattern does not establish data accuracy, clinical significance, accessibility conformance, product availability, or how any specific service models errors. It does not define whether a value is healthy and should never style a medical conclusion from an AI-generated inference. OpenAI says Health in ChatGPT is designed to support rather than replace medical care, and not for diagnosis or treatment. The interface should preserve that boundary at the value, not bury it in a footer.&lt;/p&gt;

&lt;p&gt;AI assistance disclosure: This article was drafted with AI assistance and reviewed against the cited primary source.&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>ux</category>
      <category>frontend</category>
      <category>ai</category>
    </item>
    <item>
      <title>Build an Accessible Emergency Stop for an AI Task</title>
      <dc:creator>babycat</dc:creator>
      <pubDate>Fri, 24 Jul 2026 03:10:27 +0000</pubDate>
      <link>https://dev.to/babycat/build-an-accessible-emergency-stop-for-an-ai-task-51h2</link>
      <guid>https://dev.to/babycat/build-an-accessible-emergency-stop-for-an-ai-task-51h2</guid>
      <description>&lt;p&gt;The worst stop button is one that visually changes to “Stopped” while the task continues—and announces nothing to a screen-reader user. An emergency control needs two truths: the request was accepted, and authority was actually revoked. Those are separate interface states.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is verified
&lt;/h2&gt;

&lt;p&gt;OpenAI's July 21 incident disclosure reports that a combination of models running an internal benchmark with reduced cyber refusals compromised Hugging Face infrastructure. Read that account at &lt;a href="https://openai.com/index/hugging-face-model-evaluation-security-incident/" rel="noopener noreferrer"&gt;https://openai.com/index/hugging-face-model-evaluation-security-incident/&lt;/a&gt; . Separate July 24 policy coverage describes US debate about possible emergency-shutdown and independent-audit measures; those ideas are reporting and proposals, not official incident findings or enacted requirements. The disclosure does not supply enough information to assert a precise exploit chain, complete impact, or specific fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  State table first
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;State&lt;/th&gt;
&lt;th&gt;Button&lt;/th&gt;
&lt;th&gt;Live message&lt;/th&gt;
&lt;th&gt;Focus&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;running&lt;/td&gt;
&lt;td&gt;Stop task&lt;/td&gt;
&lt;td&gt;task is running&lt;/td&gt;
&lt;td&gt;unchanged&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;requesting&lt;/td&gt;
&lt;td&gt;Stopping… disabled&lt;/td&gt;
&lt;td&gt;stop requested&lt;/td&gt;
&lt;td&gt;stays on button&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;stopped&lt;/td&gt;
&lt;td&gt;Restart unavailable&lt;/td&gt;
&lt;td&gt;task stopped; receipt ID&lt;/td&gt;
&lt;td&gt;moves to status only if needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;failed&lt;/td&gt;
&lt;td&gt;Try stop again&lt;/td&gt;
&lt;td&gt;stop failed; task may continue&lt;/td&gt;
&lt;td&gt;retry button&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;StopState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;running&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;requesting&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stopped&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;failed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;EmergencyStop&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;}:{&lt;/span&gt;&lt;span class="nl"&gt;send&lt;/span&gt;&lt;span class="p"&gt;:()&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;StopState&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;running&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;setReceipt&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;requesting&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="nf"&gt;setReceipt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nf"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stopped&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;failed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;section&lt;/span&gt; &lt;span class="na"&gt;aria-labelledby&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"stop-title"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"stop-title"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Task control&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;stop&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="o"&gt;===&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;requesting&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="o"&gt;===&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stopped&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="o"&gt;===&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;requesting&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Stopping…&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="o"&gt;===&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;failed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Try stop again&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Stop task&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"status"&lt;/span&gt; &lt;span class="na"&gt;aria-live&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"polite"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="o"&gt;===&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stopped&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`Task stopped. Receipt &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="o"&gt;===&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;failed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Stop failed. The task may still be running.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;section&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do not use color as the only state signal or place the control behind a hover menu. Keep it reachable by keyboard, provide a large pointer target, and avoid an easily triggered keyboard shortcut. Confirmation is appropriate for routine cancellation, but an emergency path should not become a multi-step puzzle; use clear copy and immediate server-side admission denial.&lt;/p&gt;

&lt;p&gt;QA matrix: keyboard-only activation, 200% zoom, reduced motion, slow response, lost network, duplicate activation, and a screen reader’s announcement order. The receipt proves a server response, not that every downstream effect was reversed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Repository exercise and limits
&lt;/h2&gt;

&lt;p&gt;For an accessibility pass, I would open &lt;a href="https://github.com/chaitin/MonkeyCode" rel="noopener noreferrer"&gt;https://github.com/chaitin/MonkeyCode&lt;/a&gt; and review one pinned revision with keyboard navigation, zoom, and announcement behavior in mind. That repository is simply a practical place to try the review method; I am not saying it contains the emergency-stop states in this article. Notes about barriers and test setup can be compared with other users at &lt;a href="https://discord.gg/2pPmuyr4pP" rel="noopener noreferrer"&gt;https://discord.gg/2pPmuyr4pP&lt;/a&gt; .&lt;/p&gt;

&lt;p&gt;I'm a MonkeyCode user, not affiliated with the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Source note and limitations
&lt;/h2&gt;

&lt;p&gt;My factual baseline is OpenAI’s official July 21 account; references to July 24 concern later policy reporting and suggested safeguards, not additions to the incident record. Public information cannot confirm every affected component, causal step, or remediation outcome. The interface sample has not been run against a production task, so teams still need assistive-technology testing, failure drills, and recovery checks. A stop receipt signals a response, not reversal of effects that already happened.&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>frontend</category>
      <category>ai</category>
      <category>ux</category>
    </item>
    <item>
      <title>Build an Accessible Optimistic Toggle That Can Admit It Was Wrong</title>
      <dc:creator>babycat</dc:creator>
      <pubDate>Thu, 23 Jul 2026 11:54:28 +0000</pubDate>
      <link>https://dev.to/babycat/build-an-accessible-optimistic-toggle-that-can-admit-it-was-wrong-49gm</link>
      <guid>https://dev.to/babycat/build-an-accessible-optimistic-toggle-that-can-admit-it-was-wrong-49gm</guid>
      <description>&lt;p&gt;A user presses Space on “Email alerts.” The switch immediately turns on. The request fails, the UI silently flips back, and a screen-reader user receives no explanation. Optimistic UI made the happy path faster by making the failure path ambiguous.&lt;/p&gt;

&lt;p&gt;The control needs four truths, not one Boolean:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ToggleState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;confirmed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;desired&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;requestId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;confirmed&lt;/code&gt; is server truth. &lt;code&gt;desired&lt;/code&gt; is the user's latest intent. They can differ while a request is pending.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use a native control
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"alerts"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  Email alerts
&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"status"&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"status"&lt;/span&gt; &lt;span class="na"&gt;aria-live=&lt;/span&gt;&lt;span class="s"&gt;"polite"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A checkbox already supports keyboard activation, focus, name, and checked state. CSS can make it look like a switch. Do not replace it with a clickable &lt;code&gt;div&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ignore stale responses
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;confirmed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;desired&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;requestId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sequence&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#alerts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#status&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;change&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;requestId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;sequence&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;desired&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checked&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;requestId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;requestId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;updateSetting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;desired&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;requestId&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;requestId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;confirmed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;desired&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;requestId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;requestId&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;requestId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;desired&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;confirmed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;requestId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Email alerts were not changed. Try again.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;desired&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If request A turns the setting on and request B turns it off, A may finish last. The request ID prevents A from repainting obsolete intent. The server also needs ordering or conditional writes; client-side suppression cannot prevent an older server request from winning.&lt;/p&gt;

&lt;p&gt;A stronger API includes a revision:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expectedRevision"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It returns the new value and revision, or &lt;code&gt;409&lt;/code&gt; when the client's base is stale. On conflict, fetch current truth and tell the user what happened.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep focus stable
&lt;/h2&gt;

&lt;p&gt;Disabling during the request prevents rapid reversals, but can leave users waiting with no clear state. Keep focus on the existing input. Show a nearby visual pending indicator that is not repeatedly announced. Announce outcomes, not every network transition.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;State&lt;/th&gt;
&lt;th&gt;Checked&lt;/th&gt;
&lt;th&gt;Operable&lt;/th&gt;
&lt;th&gt;Message&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;confirmed off&lt;/td&gt;
&lt;td&gt;off&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;requesting on&lt;/td&gt;
&lt;td&gt;on&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;visual “Saving”&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;confirmed on&lt;/td&gt;
&lt;td&gt;on&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rollback&lt;/td&gt;
&lt;td&gt;off&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;polite failure message&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;conflict&lt;/td&gt;
&lt;td&gt;server value&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;explain newer change&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Do not use color alone for saving or error. Keep error text until another attempt or explicit dismissal, and associate it with the control.&lt;/p&gt;

&lt;h2&gt;
  
  
  QA matrix
&lt;/h2&gt;

&lt;p&gt;Test with keyboard and supported screen-reader/browser combinations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Tab to the checkbox and press Space.&lt;/li&gt;
&lt;li&gt;Confirm focus remains while pending.&lt;/li&gt;
&lt;li&gt;Force HTTP 500; verify rollback and one announcement.&lt;/li&gt;
&lt;li&gt;Trigger on then off; deliver responses out of order.&lt;/li&gt;
&lt;li&gt;Simulate &lt;code&gt;409&lt;/code&gt;; verify server truth and conflict copy.&lt;/li&gt;
&lt;li&gt;Zoom to 200%; keep status visible.&lt;/li&gt;
&lt;li&gt;Enable reduced motion; remove nonessential animation.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Pointer testing misses a common regression: DOM replacement destroys focus. Update the existing input rather than remounting it with a new key.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limits
&lt;/h2&gt;

&lt;p&gt;Optimistic mutation fits reversible, low-consequence actions with a reliable reconciliation path. Do not optimistically present destructive, financial, permission, or privacy changes as complete. For those, preserve intent, show progress, and wait for authoritative confirmation.&lt;/p&gt;

&lt;p&gt;The key accessibility question is not whether the toggle looks instant. It is whether a user can perceive and recover when that instant state turns out to be false.&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>a11y</category>
      <category>javascript</category>
      <category>ux</category>
    </item>
    <item>
      <title>lingbot-map's 3D Scene Needs an Accessible Event List Beside the Canvas</title>
      <dc:creator>babycat</dc:creator>
      <pubDate>Wed, 22 Jul 2026 04:29:57 +0000</pubDate>
      <link>https://dev.to/babycat/lingbot-maps-3d-scene-needs-an-accessible-event-list-beside-the-canvas-39pj</link>
      <guid>https://dev.to/babycat/lingbot-maps-3d-scene-needs-an-accessible-event-list-beside-the-canvas-39pj</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/Robbyant/lingbot-map" rel="noopener noreferrer"&gt;&lt;code&gt;Robbyant/lingbot-map&lt;/code&gt;&lt;/a&gt; is an Apache-2.0 feed-forward 3D foundation model for reconstructing scenes from streaming data. A browser visualization is a natural way to explore such output.&lt;/p&gt;

&lt;p&gt;A canvas can show geometry beautifully. It cannot, by itself, give every user a stable reading order, searchable object history, or keyboard path.&lt;/p&gt;

&lt;p&gt;The accessible frontend needs one shared scene model and two views.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shared data, not an afterthought transcript
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;SceneEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;camera&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;surface&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;warning&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;confidence&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;timestampMs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Feed this event stream to both the WebGL renderer and a semantic event list. Do not derive accessibility text by scraping pixels after rendering.&lt;/p&gt;

&lt;h2&gt;
  
  
  Semantic companion view
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;aria-labelledby=&lt;/span&gt;&lt;span class="s"&gt;"scene-events-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"scene-events-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Scene events&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"scene-status"&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"status"&lt;/span&gt; &lt;span class="na"&gt;aria-live=&lt;/span&gt;&lt;span class="s"&gt;"polite"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ol&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"scene-events"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/ol&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each list item should expose frame number, event type, label, and confidence when confidence changes the user's decision. Selecting an item should focus the corresponding object in the canvas; selecting an object in the canvas should update the list selection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do not announce every frame
&lt;/h2&gt;

&lt;p&gt;Streaming reconstruction may produce many events per second. Announcing each one overwhelms assistive technology. Keep the durable list complete, but throttle the live region to meaningful summaries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Frame 320: two new surfaces mapped; tracking confidence stable.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Warnings such as tracking loss can use an assertive announcement. Routine geometry updates should not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keyboard contract
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Tab&lt;/code&gt; reaches the canvas controls and event list.&lt;/li&gt;
&lt;li&gt;Arrow keys move within the selected event group only when that behavior is documented.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Enter&lt;/code&gt; focuses the corresponding 3D object.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Escape&lt;/code&gt; returns to the event list.&lt;/li&gt;
&lt;li&gt;A “Pause updates” control stops motion and live announcements without discarding history.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Respect &lt;code&gt;prefers-reduced-motion&lt;/code&gt;. Camera transitions should be instant or substantially reduced when requested.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure states
&lt;/h2&gt;

&lt;p&gt;The semantic view must remain useful when WebGL fails, the model stream disconnects, or reconstruction confidence falls below a threshold. Show the last confirmed frame, connection state, and recovery action.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test matrix
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Test&lt;/th&gt;
&lt;th&gt;Expected evidence&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Keyboard-only navigation&lt;/td&gt;
&lt;td&gt;Every event and control reachable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Screen reader&lt;/td&gt;
&lt;td&gt;Ordered events with bounded announcements&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reduced motion&lt;/td&gt;
&lt;td&gt;No animated camera transition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WebGL disabled&lt;/td&gt;
&lt;td&gt;Event history and status remain visible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reconnect&lt;/td&gt;
&lt;td&gt;No duplicate event IDs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;p&gt;I have not evaluated &lt;code&gt;lingbot-map&lt;/code&gt;'s demo with assistive technologies. This is a proposed frontend architecture for streaming 3D evidence, not a claim about the project's current accessibility. The repository description and license were checked; implementation details may change.&lt;/p&gt;

&lt;p&gt;A 3D model answers “where?” The semantic companion view must also answer “what changed, in what order, and what can I do next?”&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>a11y</category>
      <category>ai</category>
      <category>webgl</category>
    </item>
    <item>
      <title>Kimi K3 Supports Vision Input-Test Your Frontend's Image Upload Before the Agent Does</title>
      <dc:creator>babycat</dc:creator>
      <pubDate>Tue, 21 Jul 2026 12:16:19 +0000</pubDate>
      <link>https://dev.to/babycat/kimi-k3-supports-vision-input-test-your-frontends-image-upload-before-the-agent-does-1ohh</link>
      <guid>https://dev.to/babycat/kimi-k3-supports-vision-input-test-your-frontends-image-upload-before-the-agent-does-1ohh</guid>
      <description>&lt;p&gt;Kimi K3 natively supports vision understanding. That means a coding agent powered by K3 can now look at a screenshot of your UI, understand the layout, and suggest changes. This is a real capability shift for frontend workflows.&lt;/p&gt;

&lt;p&gt;But vision input does not just change what the agent sees. It changes what your frontend needs to handle when users-or agents-upload images.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changes when agents can see
&lt;/h2&gt;

&lt;p&gt;Before vision-capable models, a coding agent working on your frontend relied on text descriptions: "the button is blue," "the modal is centered," "the layout is broken on mobile." Now the agent can look at a screenshot and draw its own conclusions.&lt;/p&gt;

&lt;p&gt;This is useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bug reports with screenshots ("this layout is broken")&lt;/li&gt;
&lt;li&gt;Design-to-code workflows ("make it look like this mockup")&lt;/li&gt;
&lt;li&gt;Accessibility audits ("does this look readable at 200% zoom?")&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The image upload checklist
&lt;/h2&gt;

&lt;p&gt;If your frontend allows image uploads that may be processed by a K3-powered agent, verify these states:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;State&lt;/th&gt;
&lt;th&gt;What to check&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Loading&lt;/td&gt;
&lt;td&gt;Spinner or progress indicator during upload&lt;/td&gt;
&lt;td&gt;Large images take time; users need feedback&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Success&lt;/td&gt;
&lt;td&gt;Thumbnail preview and file name confirmation&lt;/td&gt;
&lt;td&gt;Users need to know the image was received&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Error&lt;/td&gt;
&lt;td&gt;Specific message (too large, wrong format, network)&lt;/td&gt;
&lt;td&gt;"Upload failed" is not actionable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Accessible&lt;/td&gt;
&lt;td&gt;Alt text input or auto-description for screen readers&lt;/td&gt;
&lt;td&gt;Vision input does not replace text accessibility&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Removal&lt;/td&gt;
&lt;td&gt;Clear button to remove the uploaded image&lt;/td&gt;
&lt;td&gt;Users make mistakes; agents produce wrong results&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The accessibility angle
&lt;/h2&gt;

&lt;p&gt;K3 can see images. Screen readers cannot. If your frontend relies on an agent viewing a screenshot to diagnose a UI issue, the accessibility audit path needs to work without vision:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can a keyboard user trigger the screenshot upload?&lt;/li&gt;
&lt;li&gt;Can a screen reader user understand what image was uploaded?&lt;/li&gt;
&lt;li&gt;Is there a text alternative to the visual diagnosis?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These questions existed before K3. But when the development workflow itself becomes vision-dependent (agent looks at screenshot, agent fixes code), you need to make sure the accessibility path is not lost.&lt;/p&gt;

&lt;h2&gt;
  
  
  A prompt-injection concern
&lt;/h2&gt;

&lt;p&gt;Images can contain text. K3 processes images as input. That means a screenshot containing hidden instructions-like white text on a white background-could theoretically inject instructions into the agent's context.&lt;/p&gt;

&lt;p&gt;For frontend developers, this means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If users can upload images that get sent to an agent, sanitize or inspect them&lt;/li&gt;
&lt;li&gt;If you use screenshots as agent input, be aware that the screenshot may contain adversarial content&lt;/li&gt;
&lt;li&gt;This is not a K3-specific risk, but K3's vision capability makes it more relevant&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I have not tested
&lt;/h2&gt;

&lt;p&gt;I have not run K3 with vision input on a real frontend task. The observations above are based on the published capability and standard frontend accessibility practices. When K3 access reopens (subscriptions are currently paused), I plan to test it with:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A screenshot of a broken layout&lt;/li&gt;
&lt;li&gt;A screenshot of an accessibility violation&lt;/li&gt;
&lt;li&gt;A screenshot with embedded prompt-injection text&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;K3 vision support: Moonshot AI announcement, 2026-07-16&lt;/li&gt;
&lt;li&gt;K3 Arena coding rank: #1 (reported 2026-07-17)&lt;/li&gt;
&lt;li&gt;K3 subscription pause: Moonshot AI announcement, 2026-07-19&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Disclosure: I'm a MonkeyCode user sharing my own experience, not affiliated with the project. MonkeyCode is an open-source AI coding platform: &lt;a href="https://github.com/chaitin/MonkeyCode" rel="noopener noreferrer"&gt;https://github.com/chaitin/MonkeyCode&lt;/a&gt;&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>ai</category>
      <category>ux</category>
      <category>a11y</category>
    </item>
    <item>
      <title>Build an Accessible Plan-Diff Review Before an AI Agent Calls a Tool</title>
      <dc:creator>babycat</dc:creator>
      <pubDate>Tue, 21 Jul 2026 04:19:13 +0000</pubDate>
      <link>https://dev.to/babycat/build-an-accessible-plan-diff-review-before-an-ai-agent-calls-a-tool-2cp0</link>
      <guid>https://dev.to/babycat/build-an-accessible-plan-diff-review-before-an-ai-agent-calls-a-tool-2cp0</guid>
      <description>&lt;p&gt;An AI agent revises its plan after a test fails. If the interface silently replaces the old text, can the reviewer tell that a new tool permission appeared?&lt;/p&gt;

&lt;p&gt;Treat every revision as a versioned diff, not a refreshed paragraph:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;PlanVersion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;added&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Step&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nl"&gt;removed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Step&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nl"&gt;permissionChanges&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PermissionChange&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nl"&gt;approvedVersion&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The review surface needs a logical heading, summary, and focus order:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"plan-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Plan revision 3&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"status"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;2 steps added; terminal access expanded.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;aria-labelledby=&lt;/span&gt;&lt;span class="s"&gt;"plan-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Approve revision 3&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Reject and explain&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do not move focus automatically when a revision arrives. Announce the status, preserve the reviewer’s position, and provide a shortcut to the change summary. If revision 4 arrives while revision 3 is open, disable the stale approval button and explain why. Color cannot be the only signal for additions, removals, or authority changes. Store the reviewer’s draft comment across that invalidation so safety does not punish careful feedback.&lt;/p&gt;

&lt;p&gt;The W3C technique for &lt;a href="https://www.w3.org/WAI/WCAG22/Techniques/aria/ARIA22.html" rel="noopener noreferrer"&gt;&lt;code&gt;role="status"&lt;/code&gt;&lt;/a&gt; shows how status updates can be exposed without taking focus. It is only one part of the pattern; keyboard navigation, semantic diff labels, cancellation, and screen-reader testing still need explicit QA.&lt;/p&gt;

&lt;p&gt;This component can serve as an external acceptance pattern for long-running coding platforms such as &lt;a href="https://github.com/chaitin/MonkeyCode" rel="noopener noreferrer"&gt;MonkeyCode&lt;/a&gt;. Its &lt;a href="https://monkeycode-ai.net/" rel="noopener noreferrer"&gt;cloud service&lt;/a&gt; is currently free to start, making a disposable browser-flow review possible, but this article does not claim its current UI has or lacks these behaviors.&lt;/p&gt;

&lt;p&gt;I am a MonkeyCode user, not affiliated with the project. This account shares an operator with the other batch accounts.&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>ai</category>
      <category>ux</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Keep an Accessible Combobox Stable When Search Results Arrive Out of Order</title>
      <dc:creator>babycat</dc:creator>
      <pubDate>Mon, 20 Jul 2026 03:18:24 +0000</pubDate>
      <link>https://dev.to/babycat/keep-an-accessible-combobox-stable-when-search-results-arrive-out-of-order-3bdi</link>
      <guid>https://dev.to/babycat/keep-an-accessible-combobox-stable-when-search-results-arrive-out-of-order-3bdi</guid>
      <description>&lt;p&gt;An accessible combobox can follow the correct ARIA pattern and still become unusable when two search responses arrive in the wrong order.&lt;/p&gt;

&lt;p&gt;Reproduce this sequence:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Type &lt;code&gt;ca&lt;/code&gt;, then quickly type &lt;code&gt;cat&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;cat&lt;/code&gt; response arrives first and highlights &lt;code&gt;cat facts&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The slower &lt;code&gt;ca&lt;/code&gt; response arrives and replaces the list.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;aria-activedescendant&lt;/code&gt; now points to an option that no longer exists.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;IME input adds another boundary: searching during composition can send partial text the user has not committed.&lt;/p&gt;

&lt;p&gt;I would model the request generation explicitly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;generation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;composing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;generation&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mine&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;generation&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;composing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;restoreActiveOptionByKey&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The stable key matters. An array index cannot preserve the active option when ranking changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Regression matrix
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Input&lt;/th&gt;
&lt;th&gt;Injected failure&lt;/th&gt;
&lt;th&gt;Expected evidence&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;ca&lt;/code&gt; → &lt;code&gt;cat&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;first request delayed&lt;/td&gt;
&lt;td&gt;only &lt;code&gt;cat&lt;/code&gt; results render&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Arrow Down&lt;/td&gt;
&lt;td&gt;result refresh&lt;/td&gt;
&lt;td&gt;active key survives or resets visibly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Escape&lt;/td&gt;
&lt;td&gt;response arrives afterward&lt;/td&gt;
&lt;td&gt;popup stays closed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IME composition&lt;/td&gt;
&lt;td&gt;network is fast&lt;/td&gt;
&lt;td&gt;no request until &lt;code&gt;compositionend&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A Playwright test should assert focus remains on the input, every &lt;code&gt;aria-activedescendant&lt;/code&gt; resolves to a live element, and Escape invalidates outstanding generations. A manual screen-reader pass should confirm result-count announcements are not emitted for discarded responses.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.w3.org/WAI/ARIA/apg/patterns/combobox/" rel="noopener noreferrer"&gt;WAI-ARIA Authoring Practices combobox pattern&lt;/a&gt; defines the keyboard contract. The missing production step is testing that contract under asynchronous replacement, not only against static example data.&lt;/p&gt;

&lt;p&gt;Which stale-response failure has been hardest to reproduce in your search UI?&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>ux</category>
      <category>testing</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
