sounds-on-the-web by raphaelsalaja/userinterface-wiki
npx skills add https://github.com/raphaelsalaja/userinterface-wiki --skill sounds-on-the-web审查 UI 代码中音频反馈的最佳实践和可访问性。
文件:行号 格式输出检查结果| 优先级 | 类别 | 前缀 |
|---|---|---|
| 1 | 可访问性 | a11y- |
| 2 | 适用性 | appropriate- |
| 3 | 实现 | impl- |
| 4 |
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
| 权重匹配 |
weight- |
a11y-visual-equivalent每个音频提示都必须有视觉等效物;声音绝不能替代视觉反馈。
失败示例:
function SubmitButton({ onClick }) {
const handleClick = () => {
playSound("success");
onClick(); // 没有视觉确认
};
}
通过示例:
function SubmitButton({ onClick }) {
const [status, setStatus] = useState("idle");
const handleClick = () => {
playSound("success");
setStatus("success"); // 同时提供视觉反馈
onClick();
};
return <button data-status={status}>Submit</button>;
}
a11y-toggle-setting在设置中提供明确的开关以禁用声音。
失败示例:
// 无法禁用声音
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(); // 无视偏好设置直接播放
}
通过示例:
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; // 始终为最大音量
audio.play();
}
通过示例:
function playSound() {
const { volume } = usePreferences();
audio.volume = volume; // 用户控制
audio.play();
}
appropriate-no-high-frequency不要为高频交互(打字、键盘导航)添加声音。
失败示例:
function Input({ onChange }) {
const handleChange = (e) => {
playSound("keystroke"); // 每次按键都播放,令人厌烦
onChange(e);
};
}
通过示例:
function Input({ onChange }) {
// 打字时无声音 - 仅提供视觉反馈
return <input onChange={onChange} />;
}
appropriate-confirmations-only声音适用于确认操作:支付、上传、表单提交。
通过示例:
async function handlePayment() {
await processPayment();
playSound("success"); // 适用 - 重要操作
showConfirmation();
}
appropriate-errors-warnings声音适用于不容忽视的错误和警告。
通过示例:
function handleError(error: Error) {
playSound("error"); // 适用 - 需要引起注意
showErrorToast(error.message);
}
appropriate-no-decorative不要为没有信息价值的装饰性时刻添加声音。
失败示例:
function Card({ onHover }) {
return (
<div onMouseEnter={() => playSound("hover")}> {/* 装饰性的,无价值 */}
{children}
</div>
);
}
appropriate-no-punishing声音应起到告知作用,而非惩罚;避免为用户错误使用刺耳的声音。
失败示例:
function ValidationError() {
playSound("loud-buzzer"); // 惩罚性的
return <span>Invalid input</span>;
}
通过示例:
function ValidationError() {
playSound("gentle-alert"); // 告知性但不刺耳
return <span>Invalid input</span>;
}
impl-preload-audio预加载音频文件以避免播放延迟。
失败示例:
function playSound(name: string) {
const audio = new Audio(`/sounds/${name}.mp3`); // 按需加载
audio.play();
}
通过示例:
const sounds = {
success: new Audio("/sounds/success.mp3"),
error: new Audio("/sounds/error.mp3"),
};
// 在应用初始化时预加载
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; // 太响了
通过示例:
const DEFAULT_VOLUME = 0.3; // 适中的默认值
impl-reset-current-time在重新播放前重置音频的 currentTime,以允许快速触发。
失败示例:
function playSound() {
audio.play(); // 如果已经在播放,则不会重新播放
}
通过示例:
function playSound() {
audio.currentTime = 0;
audio.play();
}
weight-match-action声音的权重应与操作的重要性相匹配。
失败示例:
// 为次要操作使用响亮的号角声
function handleToggle() {
playSound("triumphant-fanfare");
setEnabled(!enabled);
}
通过示例:
// 为次要操作使用轻微的点击声
function handleToggle() {
playSound("soft-click");
setEnabled(!enabled);
}
// 为重要操作使用更丰富的声音
function handlePurchase() {
playSound("success-chime");
completePurchase();
}
weight-duration-matches-action声音的持续时间应与操作的持续时间相匹配。
失败示例:
// 为瞬时操作使用 2 秒长的声音
function handleClick() {
playSound("long-whoosh"); // 2000 毫秒
// 操作立即完成
}
通过示例:
// 为瞬时操作使用短促的声音
function handleClick() {
playSound("click"); // 50 毫秒
}
// 为过程性操作使用较长的声音
function handleUpload() {
playSound("upload-progress"); // 与上传持续时间匹配
}
审查文件时,将检查结果输出为:
文件:行号 - [规则ID] 问题描述
示例:
components/input/index.tsx:23 - [appropriate-no-high-frequency] 每次按键都播放声音
lib/sounds.ts:45 - [a11y-reduced-motion-check] 未检查 prefers-reduced-motion
在检查结果后,输出一个汇总:
| 规则 | 数量 | 严重程度 |
|---|---|---|
a11y-visual-equivalent | 2 | 高 |
appropriate-no-high-frequency | 1 | 高 |
impl-preload-audio | 3 | 中 |
| 交互类型 | 使用声音? | 原因 |
|---|---|---|
| 支付成功 | 是 | 重要的确认 |
| 表单提交 | 是 | 用户需要确认感 |
| 错误状态 | 是 | 不容忽视 |
| 通知 | 是 | 用户可能未注视屏幕 |
| 按钮点击 | 可能 | 仅适用于重要按钮 |
| 打字 | 否 | 过于频繁 |
| 悬停 | 否 | 仅为装饰性 |
| 滚动 | 否 | 过于频繁 |
| 导航 | 否 | 键盘导航会产生噪音 |
每周安装量
152
代码仓库
GitHub 星标数
633
首次出现
2026年1月28日
安全审计
安装于
opencode130
cursor123
codex120
gemini-cli115
claude-code113
github-copilot103
Review UI code for audio feedback best practices and accessibility.
file:line format| Priority | Category | Prefix |
|---|---|---|
| 1 | Accessibility | a11y- |
| 2 | Appropriateness | appropriate- |
| 3 | Implementation | impl- |
| 4 | Weight Matching | weight- |
a11y-visual-equivalentEvery audio cue must have a visual equivalent; sound never replaces visual feedback.
Fail:
function SubmitButton({ onClick }) {
const handleClick = () => {
playSound("success");
onClick(); // No visual confirmation
};
}
Pass:
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-settingProvide explicit toggle to disable sounds in settings.
Fail:
// No way to disable sounds
function App() {
return <SoundProvider>{children}</SoundProvider>;
}
Pass:
function App() {
const { soundEnabled } = usePreferences();
return (
<SoundProvider enabled={soundEnabled}>
{children}
</SoundProvider>
);
}
a11y-reduced-motion-checkRespect prefers-reduced-motion as proxy for sound sensitivity.
Fail:
function playSound(name: string) {
audio.play(); // Plays regardless of preferences
}
Pass:
function playSound(name: string) {
const prefersReducedMotion = window.matchMedia(
"(prefers-reduced-motion: reduce)"
).matches;
if (prefersReducedMotion) return;
audio.play();
}
a11y-volume-controlAllow volume adjustment independent of system volume.
Fail:
function playSound() {
audio.volume = 1; // Always full volume
audio.play();
}
Pass:
function playSound() {
const { volume } = usePreferences();
audio.volume = volume; // User-controlled
audio.play();
}
appropriate-no-high-frequencyDo not add sound to high-frequency interactions (typing, keyboard navigation).
Fail:
function Input({ onChange }) {
const handleChange = (e) => {
playSound("keystroke"); // Annoying on every keystroke
onChange(e);
};
}
Pass:
function Input({ onChange }) {
// No sound on typing - visual feedback only
return <input onChange={onChange} />;
}
appropriate-confirmations-onlySound is appropriate for confirmations: payments, uploads, form submissions.
Pass:
async function handlePayment() {
await processPayment();
playSound("success"); // Appropriate - significant action
showConfirmation();
}
appropriate-errors-warningsSound is appropriate for errors and warnings that can't be overlooked.
Pass:
function handleError(error: Error) {
playSound("error"); // Appropriate - needs attention
showErrorToast(error.message);
}
appropriate-no-decorativeDo not add sound to decorative moments with no informational value.
Fail:
function Card({ onHover }) {
return (
<div onMouseEnter={() => playSound("hover")}> {/* Decorative, no value */}
{children}
</div>
);
}
appropriate-no-punishingSound should inform, not punish; avoid harsh sounds for user mistakes.
Fail:
function ValidationError() {
playSound("loud-buzzer"); // Punishing
return <span>Invalid input</span>;
}
Pass:
function ValidationError() {
playSound("gentle-alert"); // Informative but not harsh
return <span>Invalid input</span>;
}
impl-preload-audioPreload audio files to avoid playback delay.
Fail:
function playSound(name: string) {
const audio = new Audio(`/sounds/${name}.mp3`); // Loads on demand
audio.play();
}
Pass:
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-subtleDefault volume should be subtle, not loud.
Fail:
const DEFAULT_VOLUME = 1.0; // Too loud
Pass:
const DEFAULT_VOLUME = 0.3; // Subtle default
impl-reset-current-timeReset audio currentTime before replay to allow rapid triggering.
Fail:
function playSound() {
audio.play(); // Won't replay if already playing
}
Pass:
function playSound() {
audio.currentTime = 0;
audio.play();
}
weight-match-actionSound weight should match action importance.
Fail:
// Loud fanfare for minor action
function handleToggle() {
playSound("triumphant-fanfare");
setEnabled(!enabled);
}
Pass:
// 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-actionSound duration should match action duration.
Fail:
// 2-second sound for instant action
function handleClick() {
playSound("long-whoosh"); // 2000ms
// Action completes immediately
}
Pass:
// Short sound for instant action
function handleClick() {
playSound("click"); // 50ms
}
// Longer sound for process
function handleUpload() {
playSound("upload-progress"); // Matches upload duration
}
When reviewing files, output findings as:
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
After findings, output a summary:
| Rule | Count | Severity |
|---|---|---|
a11y-visual-equivalent | 2 | HIGH |
appropriate-no-high-frequency | 1 | HIGH |
impl-preload-audio | 3 | MEDIUM |
| Interaction | Sound? | Reason |
|---|---|---|
| Payment success | Yes | Significant confirmation |
| Form submission | Yes | User needs assurance |
| Error state | Yes | Can't be overlooked |
| Notification | Yes | May not be looking at screen |
| Button click | Maybe | Only for significant buttons |
| Typing | No | Too frequent |
| Hover | No | Decorative only |
| Scroll | No | Too frequent |
| Navigation | No | Keyboard nav would be noisy |
Weekly Installs
152
Repository
GitHub Stars
633
First Seen
Jan 28, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode130
cursor123
codex120
gemini-cli115
claude-code113
github-copilot103
代码库搜索技能指南:精准查找函数、追踪依赖、理解架构与定位错误
10,900 周安装