Files
Tom Moor 64e75dac76 fix: Address various a11y findings (#11977)
* A11y improvements

* fix: Accessibility improvements for sidebar, layout, and emoji icons

- Add role="main" to content area and role="contentinfo" to right sidebar
- Add aria-expanded to sidebar Disclosure toggle button
- Add nav landmark with aria-label to shared sidebar navigation
- Render SidebarLink as button instead of div when no link target
- Hide decorative emoji icons from screen readers (aria-hidden)
- Add aria-hidden to EmojiIcon SVG element

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: Restore PopoverTrigger in FindAndReplace, add role to span

PopoverAnchor broke the find/replace popover. Revert to PopoverTrigger
and instead add role="button" and aria-label to the span so ARIA
attributes from Radix are valid on the element.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: Sidebar button styling

* fix: Use semantic list elements for References document list

Change the References list container from div to ul and wrap each
ReferenceListItem in an li element for proper screen reader semantics.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: Address PR review feedback for accessibility changes

- Heading buttons: switch from mousedown to click for keyboard access
- Heading fold: add aria-expanded attribute
- FindAndReplace: use real button element instead of span with role
- SidebarLink: branch render to avoid passing NavLink props to button
- Right sidebar: use role=complementary instead of contentinfo

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: Use translation hook for FindAndReplace, revert anchor click handler

- Use t() for aria-label in FindAndReplace button
- Revert heading anchor from click back to mousedown to avoid side effects

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: Add ts-expect-error for styled NavLink overload mismatch

The spread props on the NavLink branch cause a TypeScript overload
mismatch that was previously suppressed. Re-add the suppression.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 18:59:53 -04:00

50 lines
1.1 KiB
TypeScript

import { isUUID } from "validator";
import styled from "styled-components";
import { s } from "../styles";
type Props = {
/** The emoji to render */
emoji: string;
/** The size of the emoji, 24px is default to match standard icons */
size?: number;
className?: string;
};
/**
* EmojiIcon is a component that renders an emoji in the size of a standard icon
* in a way that can be used wherever an Icon would be.
*/
export default function EmojiIcon({ size = 24, emoji, ...rest }: Props) {
return (
<Span $size={size} {...rest}>
<SVG size={size} emoji={isUUID(emoji) ? "" : emoji} />
</Span>
);
}
const Span = styled.span<{ $size: number }>`
font-family: ${s("fontFamilyEmoji")};
display: inline-block;
width: ${(props) => props.$size}px;
height: ${(props) => props.$size}px;
`;
const SVG = ({ size, emoji }: { size: number; emoji: string }) => (
<svg
width={size}
height={size}
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<text
x="50%"
y="55%"
dominantBaseline="middle"
textAnchor="middle"
fontSize={size * 0.7}
>
{emoji}
</text>
</svg>
);