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
+1 -1
View File
@@ -45,7 +45,7 @@ function renderIcon(iconName: string): string {
id="hero-tool-search"
autocomplete="off"
placeholder={t(locale, "home.hero.searchPlaceholder")}
class="w-full rounded-2xl border border-border bg-surface py-4 pe-4 ps-12 text-base shadow-sm outline-none transition-all placeholder:text-muted focus:border-primary focus:shadow-md"
class="w-full rounded-2xl border border-border bg-surface py-4 pe-4 ps-12 text-base shadow-sm outline-none transition-all placeholder:text-muted focus:border-primary-ink focus:shadow-md"
aria-label={t(locale, "home.hero.searchPlaceholder")}
aria-expanded="false"
aria-controls="hero-search-results"
+1 -1
View File
@@ -220,7 +220,7 @@ const safeStringsJson = JSON.stringify(toolGridStrings).replace(/</g, "\\u003c")
autocomplete="off"
aria-label={t(locale, "home.toolGrid.searchLabel")}
placeholder={t(locale, "home.toolGrid.searchPlaceholder")}
class="h-13 w-full rounded-lg border border-border bg-surface py-3 ps-12 pe-12 text-base text-foreground outline-none transition-colors placeholder:text-muted focus:border-primary"
class="h-13 w-full rounded-lg border border-border bg-surface py-3 ps-12 pe-12 text-base text-foreground outline-none transition-colors placeholder:text-muted focus:border-primary-ink"
/>
<button
id="tool-command-clear"
@@ -103,22 +103,22 @@ const subjects = [
>
<div>
<label for="name" class="block text-sm font-medium">{t(locale, "contact.form.name")}</label>
<input type="text" id="name" name="name" required class="mt-1.5 w-full rounded-lg border border-border bg-background px-4 py-2.5 text-sm transition-colors focus:border-primary focus:outline-none" />
<input type="text" id="name" name="name" required class="mt-1.5 w-full rounded-lg border border-border bg-background px-4 py-2.5 text-sm transition-colors focus:border-primary-ink focus:outline-none" />
</div>
<div>
<label for="email" class="block text-sm font-medium">{t(locale, "contact.form.email")}</label>
<input type="email" id="email" name="email" required class="mt-1.5 w-full rounded-lg border border-border bg-background px-4 py-2.5 text-sm transition-colors focus:border-primary focus:outline-none" />
<input type="email" id="email" name="email" required class="mt-1.5 w-full rounded-lg border border-border bg-background px-4 py-2.5 text-sm transition-colors focus:border-primary-ink focus:outline-none" />
</div>
<div>
<label for="company" class="block text-sm font-medium">{t(locale, "contact.form.company")}</label>
<input type="text" id="company" name="company" class="mt-1.5 w-full rounded-lg border border-border bg-background px-4 py-2.5 text-sm transition-colors focus:border-primary focus:outline-none" />
<input type="text" id="company" name="company" class="mt-1.5 w-full rounded-lg border border-border bg-background px-4 py-2.5 text-sm transition-colors focus:border-primary-ink focus:outline-none" />
</div>
<div>
<label for="subject" class="block text-sm font-medium">{t(locale, "contact.form.subject")}</label>
<select id="subject" name="subject" required class="mt-1.5 w-full rounded-lg border border-border bg-background px-4 py-2.5 text-sm transition-colors focus:border-primary focus:outline-none">
<select id="subject" name="subject" required class="mt-1.5 w-full rounded-lg border border-border bg-background px-4 py-2.5 text-sm transition-colors focus:border-primary-ink focus:outline-none">
<option value="" disabled selected>{t(locale, "contact.form.selectTopic")}</option>
{subjects.map((s) => (
<option value={s}>{s}</option>
@@ -128,7 +128,7 @@ const subjects = [
<div>
<label for="message" class="block text-sm font-medium">{t(locale, "contact.form.message")}</label>
<textarea id="message" name="message" rows={5} required class="mt-1.5 w-full rounded-lg border border-border bg-background px-4 py-2.5 text-sm transition-colors focus:border-primary focus:outline-none resize-none"></textarea>
<textarea id="message" name="message" rows={5} required class="mt-1.5 w-full rounded-lg border border-border bg-background px-4 py-2.5 text-sm transition-colors focus:border-primary-ink focus:outline-none resize-none"></textarea>
</div>
<button type="submit" class="btn-primary w-full rounded-lg px-6 py-3 text-sm font-semibold">
@@ -163,7 +163,7 @@ const collectionJsonLd = {
type="text"
autocomplete="off"
placeholder={searchPlaceholder}
class="w-full rounded-xl border border-border bg-surface py-2.5 pe-4 ps-11 text-sm outline-none transition-colors placeholder:text-muted focus:border-primary"
class="w-full rounded-xl border border-border bg-surface py-2.5 pe-4 ps-11 text-sm outline-none transition-colors placeholder:text-muted focus:border-primary-ink"
/>
</div>
<div class="mt-3 flex flex-wrap gap-2">
+2
View File
@@ -351,6 +351,8 @@ code, pre, kbd {
}
.skip-link:focus {
top: 0;
outline: 2px solid var(--color-primary-ink-strong);
outline-offset: 2px;
}
/* ─── STEP CONNECTOR (How It Works) ─── */