mirror of
https://github.com/SoPat712/Speeder.git
synced 2026-08-19 11:52:31 -04:00
fix(ui): improve focus and status accessibility
This commit is contained in:
@@ -13,7 +13,7 @@ Constraints: local commits only; no push; no browser testing; one commit per fea
|
||||
- [x] Give in-player controls accessible names, keyboard behavior, and visible focus.
|
||||
- [x] Make control-bar customization operable by keyboard as well as drag and drop.
|
||||
- [x] Label generated shortcut and site-rule form controls.
|
||||
- [ ] Improve popup status announcements, focus indicators, and icon-search semantics.
|
||||
- [x] Improve popup status announcements, focus indicators, and icon-search semantics.
|
||||
|
||||
## Settings safety and validation
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
--text: #17191c;
|
||||
--muted: #626b76;
|
||||
--accent: #111827;
|
||||
--focus-ring: #2563eb;
|
||||
--switch-track-off: #c1cad6;
|
||||
--switch-track-off-border: #aeb8c5;
|
||||
--switch-track-on: #111827;
|
||||
@@ -177,9 +178,14 @@ a:visited {
|
||||
text-underline-offset: 0.14em;
|
||||
}
|
||||
|
||||
a:hover,
|
||||
a:focus {
|
||||
color: #000;
|
||||
a:hover {
|
||||
color: var(--text);
|
||||
text-decoration-thickness: 2px;
|
||||
}
|
||||
|
||||
a:focus-visible {
|
||||
outline: 2px solid var(--focus-ring);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
code {
|
||||
@@ -225,7 +231,7 @@ input[type="checkbox"]:focus-visible,
|
||||
input[type="text"]:focus,
|
||||
select:focus,
|
||||
textarea:focus {
|
||||
outline: 2px solid rgba(17, 24, 39, 0.14);
|
||||
outline: 2px solid var(--focus-ring);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
@@ -1242,6 +1248,7 @@ button.lucide-result-tile.lucide-picked {
|
||||
--text: #f2f4f6;
|
||||
--muted: #a0a8b2;
|
||||
--accent: #f2f4f6;
|
||||
--focus-ring: #93c5fd;
|
||||
--switch-track-off: #374151;
|
||||
--switch-track-off-border: #4b5563;
|
||||
--switch-track-on: #aab7c6;
|
||||
|
||||
@@ -591,7 +591,7 @@
|
||||
<div
|
||||
id="lucideIconResults"
|
||||
class="lucide-icon-results"
|
||||
role="listbox"
|
||||
role="group"
|
||||
aria-label="Matching Lucide icons"
|
||||
></div>
|
||||
<p id="lucideIconStatus" class="lucide-icon-status" aria-live="polite"></p>
|
||||
|
||||
@@ -1741,6 +1741,10 @@ function initLucideButtonIconsUI() {
|
||||
b.dataset.slug = slug;
|
||||
b.title = slug;
|
||||
b.setAttribute("aria-label", slug);
|
||||
b.setAttribute(
|
||||
"aria-pressed",
|
||||
slug === lucidePickerSelectedSlug ? "true" : "false"
|
||||
);
|
||||
if (slug === lucidePickerSelectedSlug) {
|
||||
b.classList.add("lucide-picked");
|
||||
}
|
||||
@@ -1762,6 +1766,10 @@ function initLucideButtonIconsUI() {
|
||||
resultsEl.querySelectorAll("button"),
|
||||
function (x) {
|
||||
x.classList.toggle("lucide-picked", x.dataset.slug === slug);
|
||||
x.setAttribute(
|
||||
"aria-pressed",
|
||||
x.dataset.slug === slug ? "true" : "false"
|
||||
);
|
||||
}
|
||||
);
|
||||
fetchLucideSvg(slug)
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
--border: #e2e5e9;
|
||||
--border-strong: #d4d9e0;
|
||||
--text: #17191c;
|
||||
--muted: #626b76;
|
||||
--accent: #111827;
|
||||
--muted: #626b76;
|
||||
--accent: #111827;
|
||||
--focus-ring: #2563eb;
|
||||
}
|
||||
|
||||
* {
|
||||
@@ -83,8 +84,8 @@ button:active {
|
||||
background: #f1f3f5;
|
||||
}
|
||||
|
||||
button:focus-visible {
|
||||
outline: 2px solid rgba(17, 24, 39, 0.14);
|
||||
button:focus-visible {
|
||||
outline: 2px solid var(--focus-ring);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
@@ -250,8 +251,8 @@ button:focus-visible {
|
||||
background: #f1f3f5;
|
||||
}
|
||||
|
||||
.donate-icon-btn:focus-visible {
|
||||
outline: 2px solid rgba(17, 24, 39, 0.14);
|
||||
.donate-icon-btn:focus-visible {
|
||||
outline: 2px solid var(--focus-ring);
|
||||
outline-offset: 2px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
@@ -295,8 +296,9 @@ button:focus-visible {
|
||||
--border: #2b3138;
|
||||
--border-strong: #3a414a;
|
||||
--text: #f2f4f6;
|
||||
--muted: #a0a8b2;
|
||||
--accent: #f2f4f6;
|
||||
--muted: #a0a8b2;
|
||||
--accent: #f2f4f6;
|
||||
--focus-ring: #93c5fd;
|
||||
}
|
||||
|
||||
body {
|
||||
|
||||
@@ -32,7 +32,12 @@
|
||||
<button id="enable" class="hide">Enable</button>
|
||||
<button id="disable">Disable</button>
|
||||
</div>
|
||||
<div id="status" class="popup-status hide"></div>
|
||||
<div
|
||||
id="status"
|
||||
class="popup-status hide"
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
></div>
|
||||
<div class="popup-links">
|
||||
<button id="config">Settings</button>
|
||||
<div class="popup-secondary">
|
||||
|
||||
@@ -247,6 +247,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
}
|
||||
if (def.className) btn.className = def.className;
|
||||
btn.title = btnId.charAt(0).toUpperCase() + btnId.slice(1);
|
||||
btn.setAttribute("aria-label", btn.title);
|
||||
|
||||
btn.addEventListener("click", function () {
|
||||
if (btnId === "settings") {
|
||||
|
||||
@@ -116,6 +116,9 @@ describe("options page", () => {
|
||||
|
||||
it("labels shortcut and generated site-rule controls", async () => {
|
||||
await setupOptions();
|
||||
expect(document.getElementById("lucideIconResults").getAttribute("role")).toBe(
|
||||
"group"
|
||||
);
|
||||
expect(
|
||||
document.querySelector('#display .customKey').getAttribute("aria-label")
|
||||
).toBe("Show/hide controller key");
|
||||
|
||||
@@ -35,6 +35,12 @@ describe("popup UI", () => {
|
||||
expect(
|
||||
document.querySelectorAll("#popupControlBar button").length
|
||||
).toBeGreaterThan(0);
|
||||
expect(document.getElementById("status").getAttribute("role")).toBe(
|
||||
"status"
|
||||
);
|
||||
expect(
|
||||
document.querySelector("#popupControlBar button").getAttribute("aria-label")
|
||||
).not.toBe("");
|
||||
});
|
||||
|
||||
it("shows controls when globally disabled but a whitelist site rule matches", async () => {
|
||||
|
||||
Reference in New Issue
Block a user