sounds-on-the-web について
sounds-on-the-web は、音によるフィードバックを使う UI コードを、アクセシビリティ、適切性、実装、音の重さの観点から確認するスキルです。音の妥当性や、視覚的な通知とユーザー設定の有無を点検できます。
主な機能
- 01音声のアクセシビリティ確認
- 02音を使う場面の評価
- 03音声実装のレビュー
- 04音の重さと長さの確認
- 05ファイル名と行番号付きの報告
活用シーン
- 01UI コードの音声フィードバック監査
- 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 | 中 |
サウンドの適切性マトリックス
| インタラクション | サウンド | 理由 |
|---|---|---|
| 支払い成功 | あり | 重要な確認 |
| フォーム送信 | あり | ユーザーに安心感を与える必要がある |
| エラー状態 | あり | 見落とされてはならない |
| 通知 | あり | 画面を見ていない可能性がある |
| ボタンのクリック | 場合による | 重要なボタンに限る |
| タイピング | なし | 頻度が高すぎる |
| ホバー | なし | 装飾的なものにすぎない |
| スクロール | なし | 頻度が高すぎる |
| ナビゲーション | なし | キーボード操作では音がうるさくなる |