From 317c445839d72e46a42cedb3eff9d1e89a047f1b Mon Sep 17 00:00:00 2001 From: Molecule AI Core-UIUX Date: Sun, 10 May 2026 18:27:44 +0000 Subject: [PATCH] fix(canvas): focus-visible rings across all tabs components MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix all interactive buttons across 9 tabs files — add or upgrade focus-visible rings (WCAG 2.4.7). Also normalize weak /60 and /40 opacity rings to full-color variants. Files fixed: - EventsTab.tsx: Refresh, event row expand - TracesTab.tsx: Refresh, trace row expand - ExternalConnectionSection.tsx: Show info, Rotate creds (red), dialog Cancel (accent), dialog Rotate (red) - ScheduleTab.tsx: Create/Update, Cancel - ConfigTab.tsx: Save, Cancel, Edit Agent Card, Apply env template, Save & Restart, Save, Reload - MemoryTab.tsx: Awareness expand/collapse, awareness Open, expand (collapsed), Hide/Show Advanced, Refresh, + Add, Save, Cancel, memory row expand, Save (edit), Cancel (edit), Edit, Delete, Show - ChannelsTab.tsx: Connect Channel - SkillsTab.tsx: Retry registry load - AttachmentImage.tsx: image preview button Co-Authored-By: Claude Opus 4.7 --- canvas/src/components/tabs/ChannelsTab.tsx | 2 +- canvas/src/components/tabs/ConfigTab.tsx | 14 +++++----- canvas/src/components/tabs/EventsTab.tsx | 4 +-- .../tabs/ExternalConnectionSection.tsx | 8 +++--- canvas/src/components/tabs/MemoryTab.tsx | 28 +++++++++---------- canvas/src/components/tabs/ScheduleTab.tsx | 4 +-- canvas/src/components/tabs/SkillsTab.tsx | 2 +- canvas/src/components/tabs/TracesTab.tsx | 4 +-- .../components/tabs/chat/AttachmentImage.tsx | 2 +- 9 files changed, 34 insertions(+), 34 deletions(-) diff --git a/canvas/src/components/tabs/ChannelsTab.tsx b/canvas/src/components/tabs/ChannelsTab.tsx index 39b5e459..676b0548 100644 --- a/canvas/src/components/tabs/ChannelsTab.tsx +++ b/canvas/src/components/tabs/ChannelsTab.tsx @@ -370,7 +370,7 @@ export function ChannelsTab({ workspaceId }: Props) { // Was bg-accent-strong hover:bg-accent — accent is the // LIGHTER variant; same AA contrast trap fixed in // ScheduleTab/MemoryTab/OnboardingWizard. - className="w-full text-xs py-1.5 rounded bg-accent hover:bg-accent-strong text-white transition focus:outline-none focus-visible:ring-2 focus-visible:ring-accent/60 focus-visible:ring-offset-2 focus-visible:ring-offset-surface" + className="w-full text-xs py-1.5 rounded bg-accent hover:bg-accent-strong text-white transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-surface" > Connect Channel diff --git a/canvas/src/components/tabs/ConfigTab.tsx b/canvas/src/components/tabs/ConfigTab.tsx index 33fcf16e..50ae227b 100644 --- a/canvas/src/components/tabs/ConfigTab.tsx +++ b/canvas/src/components/tabs/ConfigTab.tsx @@ -83,11 +83,11 @@ function AgentCardSection({ workspaceId }: { workspaceId: string }) { {error &&
{error}
}
+ className="px-2 py-1 bg-surface-card hover:bg-surface-elevated hover:text-ink text-[10px] rounded text-ink-mid transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1 focus-visible:ring-offset-surface">Cancel
) : ( @@ -101,7 +101,7 @@ function AgentCardSection({ workspaceId }: { workspaceId: string }) { )} {success &&
Updated
} + className="mt-2 text-[10px] text-accent hover:text-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1">Edit Agent Card )} @@ -876,7 +876,7 @@ export function ConfigTab({ workspaceId }: Props) { @@ -1016,7 +1016,7 @@ export function ConfigTab({ workspaceId }: Props) { onClick={() => handleSave(true)} disabled={!isDirty || saving} // Same accent-LIGHTER fix shipped on every other tab. - className="px-3 py-1.5 bg-accent hover:bg-accent-strong text-xs rounded text-white disabled:opacity-30 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-accent/60 focus-visible:ring-offset-1 focus-visible:ring-offset-surface" + className="px-3 py-1.5 bg-accent hover:bg-accent-strong text-xs rounded text-white disabled:opacity-30 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1 focus-visible:ring-offset-surface" > {saving ? "Restarting..." : "Save & Restart"} @@ -1024,14 +1024,14 @@ export function ConfigTab({ workspaceId }: Props) { type="button" onClick={() => handleSave(false)} disabled={!isDirty || saving} - className="px-3 py-1.5 bg-surface-card hover:bg-surface-card text-xs rounded text-ink-mid disabled:opacity-30 transition-colors" + className="px-3 py-1.5 bg-surface-card hover:bg-surface-card text-xs rounded text-ink-mid disabled:opacity-30 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1" > Save diff --git a/canvas/src/components/tabs/EventsTab.tsx b/canvas/src/components/tabs/EventsTab.tsx index 44de3410..c239153e 100644 --- a/canvas/src/components/tabs/EventsTab.tsx +++ b/canvas/src/components/tabs/EventsTab.tsx @@ -75,7 +75,7 @@ export function EventsTab({ workspaceId }: Props) { // Was hover:bg-surface-card on top of bg-surface-card — silent // no-op hover. Lift to surface-elevated, matching the Cancel // pattern from ConfirmDialog. - className="px-2 py-1 bg-surface-card hover:bg-surface-elevated hover:text-ink text-[10px] rounded text-ink-mid transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-accent/50" + className="px-2 py-1 bg-surface-card hover:bg-surface-elevated hover:text-ink text-[10px] rounded text-ink-mid transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1" > Refresh @@ -106,7 +106,7 @@ export function EventsTab({ workspaceId }: Props) { // toggles or what it controls. aria-expanded={isOpen} aria-controls={panelId} - className="w-full flex items-center gap-2 px-3 py-2 text-left rounded-t hover:bg-surface-elevated/40 focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-accent/50 transition-colors" + className="w-full flex items-center gap-2 px-3 py-2 text-left rounded-t hover:bg-surface-elevated/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1 transition-colors" > {busy === "show" ? "Loading…" : "Show connection info"} @@ -95,7 +95,7 @@ export function ExternalConnectionSection({ workspaceId }: Props) { type="button" onClick={() => setConfirmRotate(true)} disabled={busy !== null} - className="px-3 py-1.5 bg-red-900/30 hover:bg-red-900/50 border border-red-800/60 text-xs rounded text-bad disabled:opacity-30 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-red-600/60" + className="px-3 py-1.5 bg-red-900/30 hover:bg-red-900/50 border border-red-800/60 text-xs rounded text-bad disabled:opacity-30 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-red-500 focus-visible:ring-offset-1" > {busy === "rotate" ? "Rotating…" : "Rotate credentials"} @@ -124,14 +124,14 @@ export function ExternalConnectionSection({ workspaceId }: Props) { diff --git a/canvas/src/components/tabs/MemoryTab.tsx b/canvas/src/components/tabs/MemoryTab.tsx index 3dfd7034..8e560801 100644 --- a/canvas/src/components/tabs/MemoryTab.tsx +++ b/canvas/src/components/tabs/MemoryTab.tsx @@ -205,14 +205,14 @@ export function MemoryTab({ workspaceId }: Props) { @@ -245,7 +245,7 @@ export function MemoryTab({ workspaceId }: Props) { @@ -280,21 +280,21 @@ export function MemoryTab({ workspaceId }: Props) { @@ -330,7 +330,7 @@ export function MemoryTab({ workspaceId }: Props) { @@ -340,7 +340,7 @@ export function MemoryTab({ workspaceId }: Props) { setShowAdd(false); setError(null); }} - className="px-3 py-1 bg-surface-card hover:bg-surface-elevated text-xs rounded text-ink-mid" + className="px-3 py-1 bg-surface-card hover:bg-surface-elevated text-xs rounded text-ink-mid focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1" > Cancel @@ -358,7 +358,7 @@ export function MemoryTab({ workspaceId }: Props) { @@ -428,7 +428,7 @@ export function MemoryTab({ workspaceId }: Props) { @@ -436,7 +436,7 @@ export function MemoryTab({ workspaceId }: Props) { @@ -459,7 +459,7 @@ export function MemoryTab({ workspaceId }: Props) { diff --git a/canvas/src/components/tabs/ScheduleTab.tsx b/canvas/src/components/tabs/ScheduleTab.tsx index 3772a940..f7ac5c3a 100644 --- a/canvas/src/components/tabs/ScheduleTab.tsx +++ b/canvas/src/components/tabs/ScheduleTab.tsx @@ -276,7 +276,7 @@ export function ScheduleTab({ workspaceId }: Props) { // LIGHTER variant, so this hovered lighter on white text // and dropped contrast below AA. Same trap fixed in // OnboardingWizard, ConfirmDialog, ApprovalBanner. - className="text-[11px] px-3 py-1 bg-accent text-white rounded hover:bg-accent-strong disabled:opacity-40 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-accent/60 focus-visible:ring-offset-1 focus-visible:ring-offset-surface" + className="text-[11px] px-3 py-1 bg-accent text-white rounded hover:bg-accent-strong disabled:opacity-40 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1 focus-visible:ring-offset-surface" > {editId ? "Update" : "Create"} @@ -285,7 +285,7 @@ export function ScheduleTab({ workspaceId }: Props) { onClick={resetForm} // Was hover:bg-surface-card on top of bg-surface-card — // silent no-op hover. Lift to surface-elevated. - className="text-[11px] px-3 py-1 bg-surface-card text-ink-mid rounded hover:bg-surface-elevated hover:text-ink transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-accent/40 focus-visible:ring-offset-1 focus-visible:ring-offset-surface" + className="text-[11px] px-3 py-1 bg-surface-card text-ink-mid rounded hover:bg-surface-elevated hover:text-ink transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1 focus-visible:ring-offset-surface" > Cancel diff --git a/canvas/src/components/tabs/SkillsTab.tsx b/canvas/src/components/tabs/SkillsTab.tsx index f6917c43..563aff58 100644 --- a/canvas/src/components/tabs/SkillsTab.tsx +++ b/canvas/src/components/tabs/SkillsTab.tsx @@ -479,7 +479,7 @@ export function SkillsTab({ workspaceId, data }: Props) { diff --git a/canvas/src/components/tabs/TracesTab.tsx b/canvas/src/components/tabs/TracesTab.tsx index 6932ceed..84f79cd0 100644 --- a/canvas/src/components/tabs/TracesTab.tsx +++ b/canvas/src/components/tabs/TracesTab.tsx @@ -60,7 +60,7 @@ export function TracesTab({ workspaceId }: Props) { onClick={loadTraces} // Added focus-visible ring; previous version was hover-only, // invisible to keyboard users. - className="text-[10px] text-ink-mid hover:text-ink-mid rounded-sm px-1 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-accent/50" + className="text-[10px] text-ink-mid hover:text-ink-mid rounded-sm px-1 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1" > Refresh @@ -98,7 +98,7 @@ export function TracesTab({ workspaceId }: Props) { // panel. Same pattern shipped on EventsTab. aria-expanded={isOpen} aria-controls={panelId} - className="w-full px-3 py-2 flex items-center gap-2 text-left hover:bg-surface-card/60 focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-accent/50 transition-colors" + className="w-full px-3 py-2 flex items-center gap-2 text-left hover:bg-surface-card/60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1 transition-colors" > {/* Status dot uses semantic bad/good tokens — was hardcoded bg-red-400 / bg-emerald-400 which doesn't pin to the diff --git a/canvas/src/components/tabs/chat/AttachmentImage.tsx b/canvas/src/components/tabs/chat/AttachmentImage.tsx index ca4df242..a123856f 100644 --- a/canvas/src/components/tabs/chat/AttachmentImage.tsx +++ b/canvas/src/components/tabs/chat/AttachmentImage.tsx @@ -143,7 +143,7 @@ export function AttachmentImage({ workspaceId, attachment, onDownload, tone }: P type="button" onClick={() => setOpen(true)} title={`Preview ${attachment.name}`} - className={`group relative inline-block max-w-full rounded-lg overflow-hidden border focus:outline-none focus-visible:ring-2 focus-visible:ring-accent/60 ${ + className={`group relative inline-block max-w-full rounded-lg overflow-hidden border focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1 ${ tone === "user" ? "border-blue-400/30" : "border-line/50" }`} aria-label={`Open ${attachment.name} preview`}