sounds-on-the-web

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Sounds on the Web

Web端声音反馈

Review UI code for audio feedback best practices and accessibility.
审查UI代码中的音频反馈最佳实践与可访问性。

How It Works

工作原理

  1. Read the specified files (or prompt user for files/pattern)
  2. Check against all rules below
  3. Output findings in
    file:line
    format
  1. 读取指定文件(或提示用户提供文件/匹配模式)
  2. 对照以下所有规则进行检查
  3. file:line
    格式输出检查结果

Rule Categories

规则分类

PriorityCategoryPrefix
1Accessibility
a11y-
2Appropriateness
appropriate-
3Implementation
impl-
4Weight Matching
weight-
优先级分类前缀
1可访问性
a11y-
2适用性
appropriate-
3实现方式
impl-
4权重匹配
weight-

Rules

规则

Accessibility Rules

可访问性规则

a11y-visual-equivalent

a11y-visual-equivalent

Every 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-setting

Provide 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-check

Respect 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-control

Allow 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-frequency

Do 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-only

Sound 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-warnings

Sound 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-decorative

Do 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-punishing

Sound 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-audio

Preload 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-subtle

Default volume should be subtle, not loud.
Fail:
tsx
const DEFAULT_VOLUME = 1.0; // Too loud
Pass:
tsx
const DEFAULT_VOLUME = 0.3; // Subtle default
默认音量应轻柔,不宜过大。
不通过示例:
tsx
const DEFAULT_VOLUME = 1.0; // Too loud
通过示例:
tsx
const DEFAULT_VOLUME = 0.3; // Subtle default

impl-reset-current-time

impl-reset-current-time

Reset 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-action

Sound 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-action

Sound 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-motion

Summary Table

汇总表格

After findings, output a summary:
RuleCountSeverity
a11y-visual-equivalent
2HIGH
appropriate-no-high-frequency
1HIGH
impl-preload-audio
3MEDIUM
输出结果后,需提供汇总信息:
规则数量严重程度
a11y-visual-equivalent
2HIGH
appropriate-no-high-frequency
1HIGH
impl-preload-audio
3MEDIUM

Sound Appropriateness Matrix

声音适用性矩阵

InteractionSound?Reason
Payment successYesSignificant confirmation
Form submissionYesUser needs assurance
Error stateYesCan't be overlooked
NotificationYesMay not be looking at screen
Button clickMaybeOnly for significant buttons
TypingNoToo frequent
HoverNoDecorative only
ScrollNoToo frequent
NavigationNoKeyboard nav would be noisy
交互类型是否适合添加声音原因
支付成功重要操作确认
表单提交用户需要操作确认
错误状态无法被忽略
通知提醒用户可能未关注屏幕
按钮点击可选仅适用于重要按钮
打字输入频率过高
鼠标悬停仅为装饰性
滚动操作频率过高
页面导航键盘导航会产生过多噪音

References

参考资料