fix(a11y): focus indicators meet the 3:1 non-text contrast bar (#574)

Fixes #568. Adds a real --color-ring token (ink orange #A85518 light / #F0A766 dark) and sweeps all 57 focus-indicator occurrences onto it: soft opacity rings blended to 1.2-1.7:1, border-only indicators sat at 2.6-3.0:1, and four focus:ring-ring sites referenced a token that never existed. Sponsor button keeps pink via pink-700; range sliders and the file list gain their missing keyboard indicators; landing skip link and form borders hardened; the palette contrast guard pins the ring at 3:1 in both themes.
This commit is contained in:
SnapOtter
2026-07-19 16:23:15 +08:00
committed by GitHub
parent 51022628dc
commit 6339370093
35 changed files with 83 additions and 59 deletions
@@ -760,7 +760,7 @@ export function BeautifyControls({
onClick={() => applyPreset(preset)}
className={`flex flex-col items-center gap-1 py-1.5 px-1 rounded transition-colors ${
selectedPreset === preset.name
? "bg-primary/10 ring-1 ring-primary"
? "bg-primary/10 ring-1 ring-primary-ink"
: "bg-muted hover:bg-muted/80"
}`}
>
@@ -159,7 +159,7 @@ function ColorSwatches({
onClick={() => onChange(swatch)}
className={`h-6 w-6 rounded-full border-2 transition-all ${
value.toUpperCase() === swatch
? "ring-2 ring-primary ring-offset-1 border-transparent"
? "ring-2 ring-primary-ink ring-offset-1 border-transparent"
: "border-border hover:border-foreground/30"
}`}
style={{ backgroundColor: swatch }}
@@ -457,8 +457,8 @@ function CollageCell({
aria-label={`Collage cell ${cellIndex + 1}`}
className={cn(
"relative overflow-hidden transition-shadow",
isSelected && "ring-2 ring-primary ring-offset-1",
isOver && !isSelected && "ring-2 ring-primary",
isSelected && "ring-2 ring-primary-ink ring-offset-1",
isOver && !isSelected && "ring-2 ring-primary-ink",
!image && "border-2 border-dashed border-border/50",
image && !isSelected && "cursor-grab",
image && isSelected && "cursor-grab active:cursor-grabbing",
@@ -266,7 +266,7 @@ export function CollageSettings() {
onClick={() => store.setBgPreset(p.id)}
className={cn(
"w-7 h-7 rounded-md transition-all",
bgPreset === p.id && "ring-2 ring-primary ring-offset-1",
bgPreset === p.id && "ring-2 ring-primary-ink ring-offset-1",
p.border && "border border-border",
)}
style={{
@@ -106,7 +106,7 @@ export function DuotoneSettings({ onImageStyle, onImageOverlay }: DuotoneSetting
}}
className={`flex flex-col items-center gap-1 py-1.5 px-1 rounded transition-colors ${
shadow === p.shadow && highlight === p.highlight
? "bg-primary/10 ring-1 ring-primary"
? "bg-primary/10 ring-1 ring-primary-ink"
: "bg-muted hover:bg-muted/80"
}`}
>
@@ -124,7 +124,7 @@ function LabeledInput({
onChange={(e) => onChange(e.target.value)}
placeholder={placeholder}
disabled={disabled}
className="w-full px-2.5 py-1.5 rounded-md border border-input bg-background text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:opacity-50"
className="w-full px-2.5 py-1.5 rounded-md border border-border bg-background text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:opacity-50"
/>
{hint && <p className="text-[10px] text-muted-foreground">{hint}</p>}
</div>
@@ -510,14 +510,14 @@ export function EditMetadataSettings() {
<button
type="button"
onClick={() => setField("dateMode", "edit")}
className={`flex-1 text-xs py-1.5 rounded-md border ${form.dateMode === "edit" ? "bg-primary text-primary-foreground border-primary" : "border-input text-foreground"}`}
className={`flex-1 text-xs py-1.5 rounded-md border ${form.dateMode === "edit" ? "bg-primary text-primary-foreground border-primary" : "border-border text-foreground"}`}
>
Edit Dates
</button>
<button
type="button"
onClick={() => setField("dateMode", "shift")}
className={`flex-1 text-xs py-1.5 rounded-md border ${form.dateMode === "shift" ? "bg-primary text-primary-foreground border-primary" : "border-input text-foreground"}`}
className={`flex-1 text-xs py-1.5 rounded-md border ${form.dateMode === "shift" ? "bg-primary text-primary-foreground border-primary" : "border-border text-foreground"}`}
>
Shift All Dates
</button>
@@ -558,7 +558,7 @@ export function EditMetadataSettings() {
id="em-date-shift-direction"
value={form.dateShiftDirection}
onChange={(e) => setField("dateShiftDirection", e.target.value as "+" | "-")}
className="px-2.5 py-1.5 rounded-md border border-input bg-background text-sm"
className="px-2.5 py-1.5 rounded-md border border-border bg-background text-sm"
>
<option value="+">+ Forward</option>
<option value="-">- Backward</option>
@@ -685,12 +685,12 @@ export function EditMetadataSettings() {
}
}}
placeholder="Add keyword and press Enter"
className="flex-1 px-2.5 py-1.5 rounded-md border border-input bg-background text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring"
className="flex-1 px-2.5 py-1.5 rounded-md border border-border bg-background text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring"
/>
<button
type="button"
onClick={addKeyword}
className="px-2 py-1.5 rounded-md border border-input hover:bg-muted/50"
className="px-2 py-1.5 rounded-md border border-border hover:bg-muted/50"
>
<Plus className="h-3.5 w-3.5" />
</button>
@@ -776,7 +776,7 @@ export function EditMetadataSettings() {
<button
type="button"
onClick={() => loadTemplate(t.name)}
className="flex-1 text-start text-xs px-2 py-1 rounded-md border border-input hover:bg-muted/50 truncate"
className="flex-1 text-start text-xs px-2 py-1 rounded-md border border-border hover:bg-muted/50 truncate"
>
{t.name}
</button>
@@ -797,13 +797,13 @@ export function EditMetadataSettings() {
value={templateName}
onChange={(e) => setTemplateName(e.target.value)}
placeholder="Template name"
className="flex-1 px-2.5 py-1.5 rounded-md border border-input bg-background text-xs placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring"
className="flex-1 px-2.5 py-1.5 rounded-md border border-border bg-background text-xs placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring"
/>
<button
type="button"
onClick={saveTemplate}
disabled={!templateName.trim()}
className="px-2 py-1.5 rounded-md border border-input hover:bg-muted/50 disabled:opacity-50"
className="px-2 py-1.5 rounded-md border border-border hover:bg-muted/50 disabled:opacity-50"
title="Save current values as template"
>
<BookmarkPlus className="h-3.5 w-3.5" />
@@ -605,7 +605,7 @@ export function PassportPhotoSettings() {
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
placeholder="Search countries..."
className="w-full ps-7 pe-2 py-1.5 rounded border border-border bg-background text-xs text-foreground placeholder:text-muted-foreground focus:outline-none focus:border-primary"
className="w-full ps-7 pe-2 py-1.5 rounded border border-border bg-background text-xs text-foreground placeholder:text-muted-foreground focus:outline-none focus:border-ring"
/>
</div>
</div>
@@ -162,7 +162,7 @@ export function RotateControls({
commitAngleInput();
}
}}
className="w-16 text-center text-sm font-mono font-medium tabular-nums py-1.5 rounded-md bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary/50 pe-4"
className="w-16 text-center text-sm font-mono font-medium tabular-nums py-1.5 rounded-md bg-background border border-border focus:outline-none focus:ring-2 focus:ring-ring pe-4"
/>
<span className="absolute right-2 text-sm font-mono text-muted-foreground pointer-events-none">
°
@@ -196,7 +196,7 @@ export function SignaturePad({ onSave, onCancel }: SignaturePadProps) {
type="button"
aria-label={`ink ${c}`}
onClick={() => setColor(c)}
className={`h-5 w-5 rounded-full ${color === c ? "ring-2 ring-primary ring-offset-1" : ""}`}
className={`h-5 w-5 rounded-full ${color === c ? "ring-2 ring-primary-ink ring-offset-1" : ""}`}
style={{ background: c }}
/>
))}