fix: enhance accessibility for Select component with ARIA attributes
This commit is contained in:
@@ -58,7 +58,7 @@ export function DashboardView({
|
|||||||
error,
|
error,
|
||||||
mutate: mutateStats,
|
mutate: mutateStats,
|
||||||
} = useStats(initialStats);
|
} = useStats(initialStats);
|
||||||
const { data: activity } = useActivity(14, initialActivity as never);
|
const { data: activity } = useActivity(14, initialActivity);
|
||||||
const { data: reactions } = useTopReactions();
|
const { data: reactions } = useTopReactions();
|
||||||
const ambient = useAmbient();
|
const ambient = useAmbient();
|
||||||
|
|
||||||
|
|||||||
@@ -35,15 +35,18 @@ export function AmbientCanvas({
|
|||||||
let raf = 0;
|
let raf = 0;
|
||||||
let last = performance.now();
|
let last = performance.now();
|
||||||
|
|
||||||
// Lerp state
|
// Lerp state (start values match the inline style defaults on the element).
|
||||||
let r = 45,
|
let r = 45,
|
||||||
g = 212,
|
g = 212,
|
||||||
b = 191;
|
b = 191;
|
||||||
let targetR = 45,
|
|
||||||
targetG = 212,
|
|
||||||
targetB = 191;
|
|
||||||
let intensity = 0.35;
|
let intensity = 0.35;
|
||||||
let targetIntensity = 0.35;
|
// Last values actually written to the DOM — lets us skip `setProperty`
|
||||||
|
// entirely once a channel settles, so the continuous loop stops invalidating
|
||||||
|
// style/layout every frame when the ambient is static.
|
||||||
|
let lastR = -1,
|
||||||
|
lastG = -1,
|
||||||
|
lastB = -1,
|
||||||
|
lastAlpha = "-1";
|
||||||
|
|
||||||
const lerp = (a: number, b: number, t: number) => a + (b - a) * t;
|
const lerp = (a: number, b: number, t: number) => a + (b - a) * t;
|
||||||
|
|
||||||
@@ -53,21 +56,34 @@ export function AmbientCanvas({
|
|||||||
|
|
||||||
const tgt = targetRef.current;
|
const tgt = targetRef.current;
|
||||||
const [tr, tg, tb] = TONE_css[tgt.tone].split(",").map(Number);
|
const [tr, tg, tb] = TONE_css[tgt.tone].split(",").map(Number);
|
||||||
targetR = tr;
|
const targetIntensity = 0.15 + tgt.intensity * 0.85;
|
||||||
targetG = tg;
|
|
||||||
targetB = tb;
|
|
||||||
targetIntensity = 0.15 + tgt.intensity * 0.85;
|
|
||||||
|
|
||||||
r = lerp(r, targetR, 0.03);
|
r = lerp(r, tr, 0.03);
|
||||||
g = lerp(g, targetG, 0.03);
|
g = lerp(g, tg, 0.03);
|
||||||
b = lerp(b, targetB, 0.03);
|
b = lerp(b, tb, 0.03);
|
||||||
intensity = lerp(intensity, targetIntensity, 0.03);
|
intensity = lerp(intensity, targetIntensity, 0.03);
|
||||||
|
|
||||||
const root = mount;
|
const root = mount;
|
||||||
root.style.setProperty("--ab-r", String(Math.round(r)));
|
const R = Math.round(r);
|
||||||
root.style.setProperty("--ab-g", String(Math.round(g)));
|
const G = Math.round(g);
|
||||||
root.style.setProperty("--ab-b", String(Math.round(b)));
|
const B = Math.round(b);
|
||||||
root.style.setProperty("--ab-alpha", intensity.toFixed(3));
|
const A = intensity.toFixed(3);
|
||||||
|
if (R !== lastR) {
|
||||||
|
root.style.setProperty("--ab-r", String(R));
|
||||||
|
lastR = R;
|
||||||
|
}
|
||||||
|
if (G !== lastG) {
|
||||||
|
root.style.setProperty("--ab-g", String(G));
|
||||||
|
lastG = G;
|
||||||
|
}
|
||||||
|
if (B !== lastB) {
|
||||||
|
root.style.setProperty("--ab-b", String(B));
|
||||||
|
lastB = B;
|
||||||
|
}
|
||||||
|
if (A !== lastAlpha) {
|
||||||
|
root.style.setProperty("--ab-alpha", A);
|
||||||
|
lastAlpha = A;
|
||||||
|
}
|
||||||
|
|
||||||
raf = requestAnimationFrame(frame);
|
raf = requestAnimationFrame(frame);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -101,6 +101,9 @@ export function Select({
|
|||||||
ref={triggerRef}
|
ref={triggerRef}
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setOpen((o) => !o)}
|
onClick={() => setOpen((o) => !o)}
|
||||||
|
aria-haspopup="listbox"
|
||||||
|
aria-expanded={open}
|
||||||
|
aria-label={selected?.label ?? placeholder}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex w-full items-center justify-between gap-2 rounded-[8px] border border-hairline bg-surface-2 text-left text-ink transition-colors hover:border-signal/40",
|
"flex w-full items-center justify-between gap-2 rounded-[8px] border border-hairline bg-surface-2 text-left text-ink transition-colors hover:border-signal/40",
|
||||||
"focus:outline-none focus:border-signal/60",
|
"focus:outline-none focus:border-signal/60",
|
||||||
@@ -123,6 +126,7 @@ export function Select({
|
|||||||
createPortal(
|
createPortal(
|
||||||
<div
|
<div
|
||||||
ref={dropdownRef}
|
ref={dropdownRef}
|
||||||
|
role="listbox"
|
||||||
className="glass fixed z-[9999] max-h-72 overflow-auto p-1.5"
|
className="glass fixed z-[9999] max-h-72 overflow-auto p-1.5"
|
||||||
style={{
|
style={{
|
||||||
top: pos.top,
|
top: pos.top,
|
||||||
@@ -138,6 +142,8 @@ export function Select({
|
|||||||
<button
|
<button
|
||||||
key={o.value}
|
key={o.value}
|
||||||
type="button"
|
type="button"
|
||||||
|
role="option"
|
||||||
|
aria-selected={o.value === value}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onChange(o.value);
|
onChange(o.value);
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
|
|||||||
Reference in New Issue
Block a user