关于 sounds-on-the-web
sounds-on-the-web 是用于审查含音频反馈的 UI 代码的技能,涵盖无障碍、使用场景、实现方式及声音强度。它帮助开发者检查声音是否恰当,以及是否配有视觉反馈和用户控制。
主要功能
- 01检查音频无障碍要求
- 02评估声音使用场景
- 03审查音频实现实践
- 04检查声音强度和时长
- 05按文件和行号报告问题
适用场景
- 01审查 UI 代码中的音频反馈
- 02检查设置中的声音控制
- 03检查错误和确认提示音
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ raphaelsalaja/ skill --skill sounds-on-the-web安装后在 Claude Code 中输入/sounds-on-the-web确认已加载。
详细介绍
Web 上的声音
审查 UI 代码,检查音频反馈的最佳实践和无障碍性。
工作原理
- 读取指定文件(或提示用户提供文件/模式)
- 根据以下所有规则进行检查
- 按
file:line格式输出发现的问题
规则类别
| 优先级 | 类别 | 前缀 |
|---|---|---|
| 1 | 无障碍性 | a11y- |
| 2 | 适当性 | appropriate- |
| 3 | 实现 | impl- |
| 4 | 权重匹配 | weight- |
规则
无障碍规则
a11y-visual-equivalent
每个音频提示都必须有对应的视觉反馈;声音不能取代视觉反馈。
不通过:
function SubmitButton({ onClick }) {
const handleClick = () => {
playSound("success");
onClick(); // No visual confirmation
};
}
通过:
function SubmitButton({ onClick }) {
const [status, setStatus] = useState("idle");
const handleClick = () => {
playSound("success");
setStatus("success"); // Visual feedback too
onClick();
};
return <button data-status={status}>Submit</button>;
}
a11y-toggle-setting
在设置中提供明确的开关,以便关闭声音。
不通过:
// No way to disable sounds
function App() {
return <SoundProvider>{children}</SoundProvider>;
}
通过:
function App() {
const { soundEnabled } = usePreferences();
return (
<SoundProvider enabled={soundEnabled}>
{children}
</SoundProvider>
);
}
a11y-reduced-motion-check
将 prefers-reduced-motion 作为对声音敏感度的参考,并予以尊重。
不通过:
function playSound(name: string) {
audio.play(); // Plays regardless of preferences
}
通过:
function playSound(name: string) {
const prefersReducedMotion = window.matchMedia(
"(prefers-reduced-motion: reduce)"
).matches;
if (prefersReducedMotion) return;
audio.play();
}
a11y-volume-control
允许独立于系统音量调节音量。
不通过:
function playSound() {
audio.volume = 1; // Always full volume
audio.play();
}
通过:
function playSound() {
const { volume } = usePreferences();
audio.volume = volume; // User-controlled
audio.play();
}
适当性规则
appropriate-no-high-frequency
不要为高频交互(如打字、键盘导航)添加声音。
不通过:
function Input({ onChange }) {
const handleChange = (e) => {
playSound("keystroke"); // Annoying on every keystroke
onChange(e);
};
}
通过:
function Input({ onChange }) {
// No sound on typing - visual feedback only
return <input onChange={onChange} />;
}
appropriate-confirmations-only
声音适用于确认操作:付款、上传、提交表单。
通过:
async function handlePayment() {
await processPayment();
playSound("success"); // Appropriate - significant action
showConfirmation();
}
appropriate-errors-warnings
对于不容忽视的错误和警告,声音是合适的。
通过:
function handleError(error: Error) {
playSound("error"); // Appropriate - needs attention
showErrorToast(error.message);
}
appropriate-no-decorative
不要为没有信息价值的装饰性时刻添加声音。
不通过:
function Card({ onHover }) {
return (
<div onMouseEnter={() => playSound("hover")}> {/* Decorative, no value */}
{children}
</div>
);
}
appropriate-no-punishing
声音应传达信息,而非惩罚用户;避免在用户出错时播放刺耳的声音。
不通过:
function ValidationError() {
playSound("loud-buzzer"); // Punishing
return <span>Invalid input</span>;
}
通过:
function ValidationError() {
playSound("gentle-alert"); // Informative but not harsh
return <span>Invalid input</span>;
}
实现规则
impl-preload-audio
预加载音频文件,避免播放延迟。
不通过:
function playSound(name: string) {
const audio = new Audio(`/sounds/${name}.mp3`); // Loads on demand
audio.play();
}
通过:
const sounds = {
success: new Audio("/sounds/success.mp3"),
error: new Audio("/sounds/error.mp3"),
};
// Preload on app init
Object.values(sounds).forEach(audio => audio.load());
function playSound(name: keyof typeof sounds) {
sounds[name].currentTime = 0;
sounds[name].play();
}
impl-default-subtle
默认音量应轻柔,而非过响。
不通过:
const DEFAULT_VOLUME = 1.0; // Too loud
通过:
const DEFAULT_VOLUME = 0.3; // Subtle default
impl-reset-current-time
重新播放前重置音频的 currentTime,以支持快速触发。
不通过:
function playSound() {
audio.play(); // Won't replay if already playing
}
通过:
function playSound() {
audio.currentTime = 0;
audio.play();
}
权重匹配规则
weight-match-action
声音的分量应与操作的重要性相匹配。
不通过:
// Loud fanfare for minor action
function handleToggle() {
playSound("triumphant-fanfare");
setEnabled(!enabled);
}
通过:
// Subtle click for minor action
function handleToggle() {
playSound("soft-click");
setEnabled(!enabled);
}
// Richer sound for significant action
function handlePurchase() {
playSound("success-chime");
completePurchase();
}
weight-duration-matches-action
声音时长应与操作时长相匹配。
不通过:
// 2-second sound for instant action
function handleClick() {
playSound("long-whoosh"); // 2000ms
// Action completes immediately
}
通过:
// Short sound for instant action
function handleClick() {
playSound("click"); // 50ms
}
// Longer sound for process
function handleUpload() {
playSound("upload-progress"); // Matches upload duration
}
输出格式
审查文件时,按以下格式输出发现的问题:
file:line - [rule-id] description of issue
Example:
components/input/index.tsx:23 - [appropriate-no-high-frequency] Playing sound on every keystroke
lib/sounds.ts:45 - [a11y-reduced-motion-check] Not checking prefers-reduced-motion
汇总表
列出发现的问题后,输出汇总:
| 规则 | 数量 | 严重性 |
|---|---|---|
a11y-visual-equivalent | 2 | 高 |
appropriate-no-high-frequency | 1 | 高 |
impl-preload-audio | 3 | 中 |
声音适当性矩阵
| 交互 | 是否播放声音? | 原因 |
|---|---|---|
| 付款成功 | 是 | 重要的确认反馈 |
| 提交表单 | 是 | 用户需要确认 |
| 错误状态 | 是 | 不容忽视 |
| 通知 | 是 | 用户可能没有在看屏幕 |
| 按钮点击 | 视情况而定 | 仅适用于重要按钮 |
| 打字 | 否 | 频率过高 |
| 悬停 | 否 | 仅有装饰作用 |
| 滚动 | 否 | 频率过高 |
| 导航 | 否 | 键盘导航会产生噪音 |
参考资料
评论
支持 Markdown