帮助开发者为 React 与 Next.js 交互界面实现无障碍设计与可用性优化。
复制安装指令,让 AI 自动完成配置 · 推荐新手
请帮我安装 askskill 上的 "frontend-a11y" 技能: 1. 下载 https://raw.githubusercontent.com/affaan-m/ECC/main/skills/frontend-a11y/SKILL.md 2. 保存为 ~/.claude/skills/frontend-a11y/SKILL.md 3. 装好后重载技能,告诉我可以用了
请检查这段 React 表单组件的无障碍问题,并直接给出改进后的代码。重点关注语义化 HTML、label 绑定、错误提示关联、键盘可操作性和屏幕阅读器支持。
一版改进后的表单代码,并附上主要无障碍修复点说明。
请为 Next.js 项目生成一个无障碍弹窗组件,要求支持焦点陷阱、Esc 关闭、初始焦点设置、关闭后焦点返回,以及必要的 ARIA 属性。
可直接使用的弹窗组件代码,以及交互与无障碍实现说明。
下面是一个 React 下拉菜单组件,请做无障碍审查,指出问题并给出修复方案。重点检查角色属性、键盘导航、焦点管理和屏幕阅读器提示。
问题清单、修复建议,以及可参考的优化后代码片段。
Practical accessibility patterns for React and Next.js. Covers the issues most commonly flagged in code review: missing form labels, incorrect ARIA usage, non-semantic interactive elements, and broken keyboard navigation.
<input>, <select>, <textarea>)<div> or <span> with onClickaria-* attributes to any elementMissing htmlFor / id pairing and disconnected error messages are the most common issues flagged in code review.
// BAD: label has no connection to input — screen readers cannot associate them
<label>Email</label>
<input type="email" />
// GOOD: htmlFor matches input id
<label htmlFor="email">Email</label>
<input id="email" type="email" />
// BAD: visual-only asterisk conveys nothing to screen readers
<label htmlFor="email">Email *</label>
<input id="email" type="email" />
// GOOD: required enables native browser validation; aria-required signals it to screen readers
<label htmlFor="email">
Email <span aria-hidden="true">*</span>
</label>
<input id="email" type="email" required aria-required="true" />
// BAD: error text exists visually but is not linked to the input
<input id="email" type="email" />
<span className="error">Invalid email address</span>
// GOOD: aria-describedby connects input to its error message
// aria-invalid signals the invalid state to screen readers
<input
id="email"
type="email"
aria-describedby="email-error"
aria-invalid={!!error}
/>
{error && (
<span id="email-error" role="alert">
{error}
</span>
)}
interface LoginFormProps {
onSubmit: (email: string, password: string) => void;
}
export function LoginForm({ onSubmit }: LoginFormProps) {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [errors, setErrors] = useState<{ email?: string; password?: string }>({});
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
const newErrors: typeof errors = {};
if (!email) newErrors.email = 'Email is required';
if (!password) newErrors.password = 'Password is required';
if (Object.keys(newErrors).length) {
setErrors(newErrors);
return;
}
onSubmit(email, password);
};
return (
<form onSubmit={handleSubmit} noValidate>
<div>
<label htmlFor="email">
Email <span aria-hidden="true">*</span>
</label>
<input
id="email"
type="email"
value={email}
onChange={e => setEmail(e.target.value)}
aria-required="true"
aria-describedby={errors.email ? 'email-error' : undefined}
aria-invalid={!!errors.email}
autoComplete="email"
/>
{errors.email && (
<span id="email-error" role="alert">
{errors.email}
</span>
)}
</div>
<div>
<label htmlFor="password">
Password <span aria-hidden="true">*</span>
</label>
<input
id="password"
type="password"
value={password}
onChange={e => setPassword(e.target.value)}
aria-required="true"
aria-describedby={errors.password ? 'password-error' : undefined}
aria-invalid={!!errors.password}
autoComplete="current-password"
/>
{errors.password && (
<span id="password-error" role="alert">
{errors.password}
</span>
)}
</div>
…
通过双评审智能体对结果进行对抗式校验,提升输出发布前的可靠性
帮助你编写或审查符合 React 18/19 最佳实践的组件与架构。