feat: A11y fixes (#2148)

This commit is contained in:
Philip Okugbe
2026-05-04 21:21:37 +01:00
committed by GitHub
parent fe18f22dc6
commit dbe6c2d6ba
62 changed files with 587 additions and 163 deletions
@@ -1,5 +1,5 @@
import React from "react";
import { Avatar } from "@mantine/core";
import { Avatar, MantineColor } from "@mantine/core";
import { getAvatarUrl } from "@/lib/config.ts";
import { AvatarIconType } from "@/features/attachments/types/attachment.types.ts";
@@ -16,11 +16,39 @@ interface CustomAvatarProps {
mt?: string | number;
}
// `color.shade` pairs whose filled background meets WCAG AA (4.5:1) against
// white text. Avoids lime/yellow/green/orange — even their dark shades have
// weak white-text contrast.
const SAFE_INITIALS_COLORS: MantineColor[] = [
"blue.8",
"cyan.9",
"grape.7",
"indigo.7",
"pink.8",
"red.8",
"violet.7",
];
function hashName(input: string) {
let hash = 0;
for (let i = 0; i < input.length; i += 1) {
hash = (hash << 5) - hash + input.charCodeAt(i);
hash |= 0;
}
return Math.abs(hash);
}
function pickInitialsColor(name: string) {
return SAFE_INITIALS_COLORS[hashName(name) % SAFE_INITIALS_COLORS.length];
}
export const CustomAvatar = React.forwardRef<
HTMLInputElement,
CustomAvatarProps
>(({ avatarUrl, name, type, ...props }: CustomAvatarProps, ref) => {
>(({ avatarUrl, name, type, color, ...props }: CustomAvatarProps, ref) => {
const avatarLink = getAvatarUrl(avatarUrl, type);
const resolvedColor =
!color || color === "initials" ? pickInitialsColor(name ?? "") : color;
return (
<Avatar
@@ -28,7 +56,7 @@ export const CustomAvatar = React.forwardRef<
src={avatarLink}
name={name}
alt={name}
color="initials"
color={resolvedColor}
{...props}
/>
);
@@ -74,7 +74,18 @@ export function PageChildren({
/>
))}
{hasNextPage && (
<div className={classes.loadMore} onClick={() => fetchNextPage()}>
<div
className={classes.loadMore}
onClick={() => fetchNextPage()}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
fetchNextPage();
}
}}
role="button"
tabIndex={0}
>
{t("Load more")}
</div>
)}
@@ -70,11 +70,14 @@ function EmojiPicker({
closeOnEscape={true}
>
<Popover.Target ref={setTarget}>
<ActionIcon
c={actionIconProps?.c || "gray"}
variant={actionIconProps?.variant || "transparent"}
<ActionIcon
c={actionIconProps?.c || "gray"}
variant={actionIconProps?.variant || "transparent"}
size={actionIconProps?.size}
onClick={handlers.toggle}
aria-label={t("Pick emoji")}
aria-haspopup="dialog"
aria-expanded={opened}
>
{icon}
</ActionIcon>