sounds-on-the-web
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSounds on the Web
Web端声音反馈
Review UI code for audio feedback best practices and accessibility.
审查UI代码中的音频反馈最佳实践与可访问性。
How It Works
工作原理
- Read the specified files (or prompt user for files/pattern)
- Check against all rules below
- Output findings in format
file:line
- 读取指定文件(或提示用户提供文件/匹配模式)
- 对照以下所有规则进行检查
- 以格式输出检查结果
file:line
Rule Categories
规则分类
| Priority | Category | Prefix |
|---|---|---|
| 1 | Accessibility | |
| 2 | Appropriateness | |
| 3 | Implementation | |
| 4 | Weight Matching | |
| 优先级 | 分类 | 前缀 |
|---|---|---|
| 1 | 可访问性 | |
| 2 | 适用性 | |
| 3 | 实现方式 | |
| 4 | 权重匹配 | |
Rules
规则
Accessibility Rules
可访问性规则
a11y-visual-equivalent
a11y-visual-equivalenta11y-visual-equivalent
a11y-visual-equivalentEvery audio cue must have a visual equivalent; sound never replaces visual feedback.
Fail:
tsx
function SubmitButton({ onClick }) {
const handleClick = () => {
playSound("success");
onClick(); // No visual confirmation
};
}Pass:
tsx
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>;
}每个音频提示必须配有对应的视觉反馈;声音不能替代视觉反馈。
不通过示例:
tsx
function SubmitButton({ onClick }) {
const handleClick = () => {
playSound("success");
onClick(); // No visual confirmation
};
}通过示例:
tsx
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
a11y-toggle-settinga11y-toggle-setting
a11y-toggle-settingProvide explicit toggle to disable sounds in settings.
Fail:
tsx
// No way to disable sounds
function App() {
return <SoundProvider>{children}</SoundProvider>;
}Pass:
tsx
function App() {
const { soundEnabled } = usePreferences();
return (
<SoundProvider enabled={soundEnabled}>
{children}
</SoundProvider>
);
}在设置中提供明确的声音禁用开关。
不通过示例:
tsx
// No way to disable sounds
function App() {
return <SoundProvider>{children}</SoundProvider>;
}通过示例:
tsx
function App() {
const { soundEnabled } = usePreferences();
return (
<SoundProvider enabled={soundEnabled}>
{children}
</SoundProvider>
);
}a11y-reduced-motion-check
a11y-reduced-motion-checka11y-reduced-motion-check
a11y-reduced-motion-checkRespect prefers-reduced-motion as proxy for sound sensitivity.
Fail:
tsx
function playSound(name: string) {
audio.play(); // Plays regardless of preferences
}Pass:
tsx
function playSound(name: string) {
const prefersReducedMotion = window.matchMedia(
"(prefers-reduced-motion: reduce)"
).matches;
if (prefersReducedMotion) return;
audio.play();
}尊重设置,将其作为声音敏感度的判断依据。
prefers-reduced-motion不通过示例:
tsx
function playSound(name: string) {
audio.play(); // Plays regardless of preferences
}通过示例:
tsx
function playSound(name: string) {
const prefersReducedMotion = window.matchMedia(
"(prefers-reduced-motion: reduce)"
).matches;
if (prefersReducedMotion) return;
audio.play();
}a11y-volume-control
a11y-volume-controla11y-volume-control
a11y-volume-controlAllow volume adjustment independent of system volume.
Fail:
tsx
function playSound() {
audio.volume = 1; // Always full volume
audio.play();
}Pass:
tsx
function playSound() {
const { volume } = usePreferences();
audio.volume = volume; // User-controlled
audio.play();
}允许独立于系统音量调节声音大小。
不通过示例:
tsx
function playSound() {
audio.volume = 1; // Always full volume
audio.play();
}通过示例:
tsx
function playSound() {
const { volume } = usePreferences();
audio.volume = volume; // User-controlled
audio.play();
}Appropriateness Rules
适用性规则
appropriate-no-high-frequency
appropriate-no-high-frequencyappropriate-no-high-frequency
appropriate-no-high-frequencyDo not add sound to high-frequency interactions (typing, keyboard navigation).
Fail:
tsx
function Input({ onChange }) {
const handleChange = (e) => {
playSound("keystroke"); // Annoying on every keystroke
onChange(e);
};
}Pass:
tsx
function Input({ onChange }) {
// No sound on typing - visual feedback only
return <input onChange={onChange} />;
}不要为高频交互(如打字、键盘导航)添加声音。
不通过示例:
tsx
function Input({ onChange }) {
const handleChange = (e) => {
playSound("keystroke"); // Annoying on every keystroke
onChange(e);
};
}通过示例:
tsx
function Input({ onChange }) {
// No sound on typing - visual feedback only
return <input onChange={onChange} />;
}appropriate-confirmations-only
appropriate-confirmations-onlyappropriate-confirmations-only
appropriate-confirmations-onlySound is appropriate for confirmations: payments, uploads, form submissions.
Pass:
tsx
async function handlePayment() {
await processPayment();
playSound("success"); // Appropriate - significant action
showConfirmation();
}声音适用于确认类场景:支付完成、文件上传、表单提交。
通过示例:
tsx
async function handlePayment() {
await processPayment();
playSound("success"); // Appropriate - significant action
showConfirmation();
}appropriate-errors-warnings
appropriate-errors-warningsappropriate-errors-warnings
appropriate-errors-warningsSound is appropriate for errors and warnings that can't be overlooked.
Pass:
tsx
function handleError(error: Error) {
playSound("error"); // Appropriate - needs attention
showErrorToast(error.message);
}声音适用于无法被忽略的错误与警告场景。
通过示例:
tsx
function handleError(error: Error) {
playSound("error"); // Appropriate - needs attention
showErrorToast(error.message);
}appropriate-no-decorative
appropriate-no-decorativeappropriate-no-decorative
appropriate-no-decorativeDo not add sound to decorative moments with no informational value.
Fail:
tsx
function Card({ onHover }) {
return (
<div onMouseEnter={() => playSound("hover")}> {/* Decorative, no value */}
{children}
</div>
);
}不要为无信息价值的装饰性场景添加声音。
不通过示例:
tsx
function Card({ onHover }) {
return (
<div onMouseEnter={() => playSound("hover")}> {/* Decorative, no value */}
{children}
</div>
);
}appropriate-no-punishing
appropriate-no-punishingappropriate-no-punishing
appropriate-no-punishingSound should inform, not punish; avoid harsh sounds for user mistakes.
Fail:
tsx
function ValidationError() {
playSound("loud-buzzer"); // Punishing
return <span>Invalid input</span>;
}Pass:
tsx
function ValidationError() {
playSound("gentle-alert"); // Informative but not harsh
return <span>Invalid input</span>;
}声音应起到告知作用,而非惩罚;避免为用户错误使用刺耳的声音。
不通过示例:
tsx
function ValidationError() {
playSound("loud-buzzer"); // Punishing
return <span>Invalid input</span>;
}通过示例:
tsx
function ValidationError() {
playSound("gentle-alert"); // Informative but not harsh
return <span>Invalid input</span>;
}Implementation Rules
实现规则
impl-preload-audio
impl-preload-audioimpl-preload-audio
impl-preload-audioPreload audio files to avoid playback delay.
Fail:
tsx
function playSound(name: string) {
const audio = new Audio(`/sounds/${name}.mp3`); // Loads on demand
audio.play();
}Pass:
tsx
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();
}预加载音频文件以避免播放延迟。
不通过示例:
tsx
function playSound(name: string) {
const audio = new Audio(`/sounds/${name}.mp3`); // Loads on demand
audio.play();
}通过示例:
tsx
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
impl-default-subtleimpl-default-subtle
impl-default-subtleDefault volume should be subtle, not loud.
Fail:
tsx
const DEFAULT_VOLUME = 1.0; // Too loudPass:
tsx
const DEFAULT_VOLUME = 0.3; // Subtle default默认音量应轻柔,不宜过大。
不通过示例:
tsx
const DEFAULT_VOLUME = 1.0; // Too loud通过示例:
tsx
const DEFAULT_VOLUME = 0.3; // Subtle defaultimpl-reset-current-time
impl-reset-current-timeimpl-reset-current-time
impl-reset-current-timeReset audio currentTime before replay to allow rapid triggering.
Fail:
tsx
function playSound() {
audio.play(); // Won't replay if already playing
}Pass:
tsx
function playSound() {
audio.currentTime = 0;
audio.play();
}重新播放前重置音频的currentTime,以支持快速触发播放。
不通过示例:
tsx
function playSound() {
audio.play(); // Won't replay if already playing
}通过示例:
tsx
function playSound() {
audio.currentTime = 0;
audio.play();
}Weight Matching Rules
权重匹配规则
weight-match-action
weight-match-actionweight-match-action
weight-match-actionSound weight should match action importance.
Fail:
tsx
// Loud fanfare for minor action
function handleToggle() {
playSound("triumphant-fanfare");
setEnabled(!enabled);
}Pass:
tsx
// Subtle click for minor action
function handleToggle() {
playSound("soft-click");
setEnabled(!enabled);
}
// Richer sound for significant action
function handlePurchase() {
playSound("success-chime");
completePurchase();
}声音的强度应与操作的重要性匹配。
不通过示例:
tsx
// Loud fanfare for minor action
function handleToggle() {
playSound("triumphant-fanfare");
setEnabled(!enabled);
}通过示例:
tsx
// 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
weight-duration-matches-actionweight-duration-matches-action
weight-duration-matches-actionSound duration should match action duration.
Fail:
tsx
// 2-second sound for instant action
function handleClick() {
playSound("long-whoosh"); // 2000ms
// Action completes immediately
}Pass:
tsx
// Short sound for instant action
function handleClick() {
playSound("click"); // 50ms
}
// Longer sound for process
function handleUpload() {
playSound("upload-progress"); // Matches upload duration
}声音的时长应与操作的时长匹配。
不通过示例:
tsx
// 2-second sound for instant action
function handleClick() {
playSound("long-whoosh"); // 2000ms
// Action completes immediately
}通过示例:
tsx
// Short sound for instant action
function handleClick() {
playSound("click"); // 50ms
}
// Longer sound for process
function handleUpload() {
playSound("upload-progress"); // Matches upload duration
}Output Format
输出格式
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审查文件时,按以下格式输出结果:
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-motionSummary Table
汇总表格
After findings, output a summary:
| Rule | Count | Severity |
|---|---|---|
| 2 | HIGH |
| 1 | HIGH |
| 3 | MEDIUM |
输出结果后,需提供汇总信息:
| 规则 | 数量 | 严重程度 |
|---|---|---|
| 2 | HIGH |
| 1 | HIGH |
| 3 | MEDIUM |
Sound Appropriateness Matrix
声音适用性矩阵
| 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 |
| 交互类型 | 是否适合添加声音 | 原因 |
|---|---|---|
| 支付成功 | 是 | 重要操作确认 |
| 表单提交 | 是 | 用户需要操作确认 |
| 错误状态 | 是 | 无法被忽略 |
| 通知提醒 | 是 | 用户可能未关注屏幕 |
| 按钮点击 | 可选 | 仅适用于重要按钮 |
| 打字输入 | 否 | 频率过高 |
| 鼠标悬停 | 否 | 仅为装饰性 |
| 滚动操作 | 否 | 频率过高 |
| 页面导航 | 否 | 键盘导航会产生过多噪音 |