mcpField
S
sounds-on-the-web

sounds-on-the-web

github.com/raphaelsalaja设计与 UI42212 次安装今天更新
代码仓库

关于 sounds-on-the-web

sounds-on-the-web 是用于审查含音频反馈的 UI 代码的技能,涵盖无障碍、使用场景、实现方式及声音强度。它帮助开发者检查声音是否恰当,以及是否配有视觉反馈和用户控制。

主要功能

  1. 01检查音频无障碍要求
  2. 02评估声音使用场景
  3. 03审查音频实现实践
  4. 04检查声音强度和时长
  5. 05按文件和行号报告问题

适用场景

  1. 01审查 UI 代码中的音频反馈
  2. 02检查设置中的声音控制
  3. 03检查错误和确认提示音

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/raphaelsalaja/skill --skill sounds-on-the-web

安装后在 Claude Code 中输入/sounds-on-the-web确认已加载。

详细介绍

Web 上的声音

审查 UI 代码,检查音频反馈的最佳实践和无障碍性。

工作原理

  1. 读取指定文件(或提示用户提供文件/模式)
  2. 根据以下所有规则进行检查
  3. 按 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-equivalent2高
appropriate-no-high-frequency1高
impl-preload-audio3中

声音适当性矩阵

交互是否播放声音?原因
付款成功是重要的确认反馈
提交表单是用户需要确认
错误状态是不容忽视
通知是用户可能没有在看屏幕
按钮点击视情况而定仅适用于重要按钮
打字否频率过高
悬停否仅有装饰作用
滚动否频率过高
导航否键盘导航会产生噪音

参考资料

评论

支持 Markdown