Skip to content

Commit f2a4724

Browse files
feat(web): improve input component accessibility
- Use `React.useId` to generate unique IDs for input fields. - Associate labels with inputs using `htmlFor` and `id`. - Add `aria-invalid` and `aria-describedby` to associate error messages with inputs. - Ensure error messages have unique IDs.
1 parent a035ec0 commit f2a4724

File tree

1 file changed

+28
-5
lines changed

1 file changed

+28
-5
lines changed

web/components/ui/Input.tsx

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react';
1+
import React, { useId } from 'react';
22
import { useTheme } from '../../contexts/ThemeContext';
33
import { THEMES } from '../../constants';
44

@@ -7,8 +7,11 @@ interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
77
error?: string;
88
}
99

10-
export const Input: React.FC<InputProps> = ({ label, error, className = '', ...props }) => {
10+
export const Input: React.FC<InputProps> = ({ label, error, className = '', id, ...props }) => {
1111
const { style, mode } = useTheme();
12+
const generatedId = useId();
13+
const inputId = id || generatedId;
14+
const errorId = `${inputId}-error`;
1215

1316
let inputStyles = "w-full outline-none transition-all duration-200";
1417

@@ -20,9 +23,29 @@ export const Input: React.FC<InputProps> = ({ label, error, className = '', ...p
2023

2124
return (
2225
<div className="flex flex-col gap-1 w-full">
23-
{label && <label className={`text-sm font-semibold ${style === THEMES.NEOBRUTALISM ? 'uppercase' : 'ml-1 opacity-80'}`}>{label}</label>}
24-
<input className={`${inputStyles} ${className}`} {...props} />
25-
{error && <span className="text-red-500 text-xs font-bold mt-1">{error}</span>}
26+
{label && (
27+
<label
28+
htmlFor={inputId}
29+
className={`text-sm font-semibold ${style === THEMES.NEOBRUTALISM ? 'uppercase' : 'ml-1 opacity-80'}`}
30+
>
31+
{label}
32+
</label>
33+
)}
34+
<input
35+
id={inputId}
36+
aria-invalid={!!error}
37+
aria-describedby={error ? errorId : undefined}
38+
className={`${inputStyles} ${className}`}
39+
{...props}
40+
/>
41+
{error && (
42+
<span
43+
id={errorId}
44+
className="text-red-500 text-xs font-bold mt-1"
45+
>
46+
{error}
47+
</span>
48+
)}
2649
</div>
2750
);
2851
};

0 commit comments

Comments
 (0)