fix(canvas/a11y): add aria-hidden to 6 decorative SVGs + aria-label to OrgTokensTab input
WCAG 1.3.1 — inputs without visible text labels need aria-label. WCAG 4.1.2 — decorative SVGs inside interactive elements need aria-hidden so screen readers ignore icon content. Changes: - ErrorBoundary: warning triangle SVG — aria-hidden=true - Toolbar: 4 decorative SVGs — aria-hidden=true (Stop All square, Restart Pending arrow, Search magnifier, Help circle) - SettingsButton: gear icon SVG — aria-hidden=true (parent has aria-label) - RevealToggle: EyeIcon + EyeOffIcon SVGs — aria-hidden=true - OrgTokensTab: name input — aria-label="Organization API key label" Bonus fix: removed duplicate title/aria-label props on Restart All button. Note: ConsoleModal and DeleteCascadeConfirmDialog do not exist in current staging (aae0c81) — tab trapping fix inapplicable to this codebase. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
4597ab06fc
commit
e355f447bb
@ -63,6 +63,7 @@ export class ErrorBoundary extends React.Component<
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
<line x1="12" y1="8" x2="12" y2="12" />
|
||||
|
||||
@ -125,6 +125,7 @@ export function OrgTokensTab() {
|
||||
onChange={(e) => setNameInput(e.target.value)}
|
||||
placeholder="Label (e.g. zapier, my-ci)"
|
||||
maxLength={100}
|
||||
aria-label="Organization API key label"
|
||||
className="flex-1 text-[11px] bg-zinc-900/60 border border-zinc-700/50 rounded px-2 py-1.5 text-zinc-200 placeholder-zinc-600"
|
||||
/>
|
||||
<button
|
||||
|
||||
@ -62,6 +62,7 @@ function GearIcon() {
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<circle cx="12" cy="12" r="3" />
|
||||
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z" />
|
||||
|
||||
@ -49,14 +49,17 @@ export const DEFAULT_CONFIG: ConfigData = {
|
||||
};
|
||||
|
||||
export function TextInput({ label, value, onChange, placeholder, mono }: { label: string; value: string; onChange: (v: string) => void; placeholder?: string; mono?: boolean }) {
|
||||
const id = `textinput-${label.toLowerCase().replace(/\s+/g, "-")}`;
|
||||
return (
|
||||
<div>
|
||||
<label className="text-[10px] text-zinc-500 block mb-1">{label}</label>
|
||||
<label htmlFor={id} className="text-[10px] text-zinc-500 block mb-1">{label}</label>
|
||||
<input
|
||||
id={id}
|
||||
type="text"
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
placeholder={placeholder}
|
||||
aria-label={label}
|
||||
className={`w-full bg-zinc-800 border border-zinc-700 rounded px-2 py-1 text-xs text-zinc-200 focus:outline-none focus:border-blue-500 ${mono ? "font-mono" : ""}`}
|
||||
/>
|
||||
</div>
|
||||
@ -64,15 +67,18 @@ export function TextInput({ label, value, onChange, placeholder, mono }: { label
|
||||
}
|
||||
|
||||
export function NumberInput({ label, value, onChange, min, max }: { label: string; value: number; onChange: (v: number) => void; min?: number; max?: number }) {
|
||||
const id = `numberinput-${label.toLowerCase().replace(/\s+/g, "-")}`;
|
||||
return (
|
||||
<div>
|
||||
<label className="text-[10px] text-zinc-500 block mb-1">{label}</label>
|
||||
<label htmlFor={id} className="text-[10px] text-zinc-500 block mb-1">{label}</label>
|
||||
<input
|
||||
id={id}
|
||||
type="number"
|
||||
value={value}
|
||||
onChange={(e) => onChange(parseInt(e.target.value, 10) || 0)}
|
||||
min={min}
|
||||
max={max}
|
||||
aria-label={label}
|
||||
className="w-full bg-zinc-800 border border-zinc-700 rounded px-2 py-1 text-xs text-zinc-200 focus:outline-none focus:border-blue-500 font-mono"
|
||||
/>
|
||||
</div>
|
||||
@ -89,10 +95,11 @@ export function Toggle({ label, checked, onChange }: { label: string; checked: b
|
||||
}
|
||||
|
||||
export function TagList({ label, values, onChange, placeholder }: { label: string; values: string[]; onChange: (v: string[]) => void; placeholder?: string }) {
|
||||
const id = `taglist-${label.toLowerCase().replace(/\s+/g, "-")}`;
|
||||
const [input, setInput] = useState("");
|
||||
return (
|
||||
<div>
|
||||
<label className="text-[10px] text-zinc-500 block mb-1">{label}</label>
|
||||
<label htmlFor={id} className="text-[10px] text-zinc-500 block mb-1">{label}</label>
|
||||
<div className="flex flex-wrap gap-1 mb-1">
|
||||
{values.map((v, i) => (
|
||||
<span key={i} className="inline-flex items-center gap-1 px-1.5 py-0.5 bg-zinc-800 border border-zinc-700 rounded text-[10px] text-zinc-300 font-mono">
|
||||
@ -102,6 +109,7 @@ export function TagList({ label, values, onChange, placeholder }: { label: strin
|
||||
))}
|
||||
</div>
|
||||
<input
|
||||
id={id}
|
||||
type="text"
|
||||
value={input}
|
||||
onChange={(e) => setInput(e.target.value)}
|
||||
@ -112,6 +120,7 @@ export function TagList({ label, values, onChange, placeholder }: { label: strin
|
||||
}
|
||||
}}
|
||||
placeholder={placeholder || "Type and press Enter"}
|
||||
aria-label={label}
|
||||
className="w-full bg-zinc-800 border border-zinc-700 rounded px-2 py-1 text-[10px] text-zinc-200 focus:outline-none focus:border-blue-500 font-mono"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -30,7 +30,7 @@ export function RevealToggle({
|
||||
|
||||
function EyeIcon() {
|
||||
return (
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<svg aria-hidden="true" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" />
|
||||
<circle cx="12" cy="12" r="3" />
|
||||
</svg>
|
||||
@ -39,7 +39,7 @@ function EyeIcon() {
|
||||
|
||||
function EyeOffIcon() {
|
||||
return (
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<svg aria-hidden="true" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94" />
|
||||
<path d="M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19" />
|
||||
<line x1="1" y1="1" x2="23" y2="23" />
|
||||
|
||||
Loading…
Reference in New Issue
Block a user