Skip to content

Commit adfb46f

Browse files
committed
Refactor screen reader announcement message into a reusable hook
1 parent 55489d7 commit adfb46f

File tree

2 files changed

+19
-18
lines changed

2 files changed

+19
-18
lines changed

src/components/CopyCodeButton/index.tsx

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { useState } from 'preact/hooks';
2+
import { useLiveRegion } from '../hooks/useLiveRegion';
23
import CircleButton from "../CircleButton";
34

45
interface CopyCodeButtonProps {
@@ -12,23 +13,7 @@ export const CopyCodeButton = ({
1213
}: CopyCodeButtonProps) => {
1314
const [isCopied, setIsCopied] = useState(false);
1415

15-
// const liveId = useId();
16-
const [liveId] = useState(
17-
() => 'copy-live-' + Math.random().toString(36).slice(2)
18-
);
19-
20-
/** Write a message into the live region so screen readers will announce it */
21-
const announce = (message: string) => {
22-
const region = document.getElementById(liveId) as HTMLSpanElement | null;
23-
if (!region) return;
24-
25-
region.textContent = message;
26-
27-
// Clear the text after 1 s so a future announcement will fire again
28-
setTimeout(() => {
29-
region.textContent = '';
30-
}, 1000);
31-
};
16+
const { ref: liveRegionRef, announce } = useLiveRegion<HTMLSpanElement>();
3217

3318
const copyTextToClipboard = async () => {
3419
console.log('Copy button clicked');
@@ -103,7 +88,7 @@ export const CopyCodeButton = ({
10388
)}
10489
</CircleButton>
10590
{/* Visually hidden live region for accessibility announcements */}
106-
<span id={liveId} aria-live="polite" class="sr-only" />
91+
<span ref={liveRegionRef} aria-live="polite" class="sr-only" />
10792
</>
10893
);
10994
};

src/components/hooks/useLiveRegion.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { useRef } from 'preact/hooks';
2+
3+
export function useLiveRegion<T extends HTMLElement = HTMLElement>() {
4+
const ref = useRef<T | null>(null);
5+
6+
const announce = (message: string, clearMessage = 1000) => {
7+
const node = ref.current;
8+
if (!node) return;
9+
node.textContent = message;
10+
setTimeout(() => {
11+
if (node) node.textContent = '';
12+
}, clearMessage);
13+
};
14+
15+
return { ref, announce };
16+
}

0 commit comments

Comments
 (0)