UI Sound Kit
用 Web Audio 即時合成 click、hover、success、error 等輕量介面音效,內建音量控制、全域開關與偏好尊重,零音檔零額外請求。
把這個效果裝進你的專案
在專案資料夾執行指令,CLI 會加入原始碼與依賴套件,再依下方使用範例引用元件。
npx webberui@latest add ui-sound-kitUI Sound Kit 是 React 的介面音效(UI sound effects)套件,用瀏覽器的 Web Audio API 即時合成按鍵、通知、成功與錯誤提示音,透過 React Context 提供 useSound() hook,附帶的按鈕以 Tailwind CSS 撰寫。
Playground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
<SoundButton/>
完整範例包含預覽的排版與資料。先安裝元件,再把範例檔貼入你的專案。
適用情境
- 表單送出、付款完成、上傳失敗等關鍵操作的回饋音,讓結果更容易被注意到。
- 聊天、客服或協作工具收到新訊息時的通知提示音。
- 遊戲化介面、互動式導覽或兒童教育產品的按鍵音效。
- 設定頁的「介面音效」開關與音量調整,偏好會自動記在瀏覽器裡。
- 不適合:播放自訂音檔或背景音樂。內建音效是固定的六種合成音,沒有載入音檔的 API。
安裝
npx webberui@latest add https://webberui.com/r/ui-sound-kit.json或在 components.json 設定 registries 後,改用 @webberui/ui-sound-kit 安裝。
使用
在需要音效的範圍外層包一次 SoundProvider,內部任何元件即可透過 useSound() 取得 play() 與開關/音量控制:
import {
SoundProvider,
useSound,
} from "@/components/ui/ui-sound-kit";
function App() {
return (
<SoundProvider defaultVolume={0.5}>
<SaveButton />
</SoundProvider>
);
}
function SaveButton() {
const { play } = useSound();
return (
<button
onClick={async () => {
try {
await save();
play("success");
} catch {
play("error");
}
}}
>
儲存
</button>
);
}內建音效名稱:"click"、"hover"、"success"、"error"、"toggle"、"notification"(可從 SOUND_NAMES 取得完整清單)。
內建元件
SoundButton 是會發聲的按鈕,點擊(與可選的 hover)時自動播放;SoundToggle 是全域開關按鈕:
import {
SoundButton,
SoundToggle,
} from "@/components/ui/ui-sound-kit";
<SoundButton sound="click" hoverSound="hover">
播放
</SoundButton>
<SoundToggle />範例
在 Next.js 根版面掛上 Provider
SoundProvider 是 client component,可以直接放在 Server Component 的 app/layout.tsx 裡包住 children。
import { SoundProvider } from "@/components/ui/ui-sound-kit";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="zh-Hant">
<body>
<SoundProvider defaultVolume={0.4}>{children}</SoundProvider>
</body>
</html>
);
}設定頁的開關與音量滑桿
用 useSound() 回傳的 volume 與 setVolume 接一個原生滑桿,SoundToggle 負責全域開關。
"use client";
import { SoundToggle, useSound } from "@/components/ui/ui-sound-kit";
export function SoundSettings() {
const { volume, setVolume, enabled } = useSound();
return (
<div className="flex items-center gap-3">
<SoundToggle />
<input
type="range"
min={0}
max={1}
step={0.05}
value={volume}
disabled={!enabled}
onChange={(e) => setVolume(Number(e.target.value))}
aria-label="介面音效音量"
/>
</div>
);
}收到新訊息時播放通知音
play() 的第二個參數可以設定這一次的音量倍率,疊在全域音量之上。
"use client";
import * as React from "react";
import { useSound } from "@/components/ui/ui-sound-kit";
export function useMessageSound(socket: WebSocket) {
const { play } = useSound();
React.useEffect(() => {
const onMessage = () => play("notification", { volume: 0.6 });
socket.addEventListener("message", onMessage);
return () => socket.removeEventListener("message", onMessage);
}, [socket, play]);
}Props
SoundProvider
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
defaultEnabled | boolean | true | 初始是否開啟音效(有持久化偏好時掛載後覆蓋) |
defaultVolume | number | 0.5 | 初始全域音量 0~1 |
storageKey | string | null | "wb-sound-kit" | 持久化開關與音量的 localStorage 鍵;null 停用持久化 |
respectReducedMotion | boolean | true | 系統開啟「減少動態效果」時是否一併靜音 |
useSound() 回傳
| 名稱 | 型別 | 說明 |
|---|---|---|
play | (name, options?) => void | 合成並播放內建音效,options.volume 為單次音量倍率 |
enabled | boolean | 全域音效是否開啟 |
setEnabled | (enabled: boolean) => void | 開啟/關閉全域音效 |
volume | number | 全域音量 0~1 |
setVolume | (volume: number) => void | 設定全域音量(自動夾在 0~1) |
SoundButton
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
sound | SoundName | "click" | 點擊時播放的音效 |
hoverSound | SoundName | — | 指定時,游標移入也會播放此音效 |
其餘原生 button 屬性皆透傳。SoundToggle 亦透傳原生 button 屬性,並以 chime(預設 true)控制開啟時是否播放確認音。
細節
- 即時合成,零資源:以 Web Audio 的
OscillatorNode+GainNode即時合成音效,不載入任何音檔、不發任何額外請求。 - 惰性初始化:
AudioContext於首次play()(通常來自使用者點擊手勢)才建立並自動resume,符合瀏覽器自動播放政策;分頁切回若被suspend也會嘗試恢復。 - 單一音訊圖:整個 Provider 共用一個
AudioContext與主音量節點;每次播放另接一段獨立 bus 增益,套用單次音量倍率後回收。 - 持久化偏好:開關與音量預設寫入
localStorage,重新整理後沿用;可用storageKey={null}關閉。
可及性
SoundToggle以aria-pressed表示開關狀態,並隨狀態切換aria-label(開啟音效/關閉音效)與圖示。- 音量滑桿為原生
<input type="range">,帶aria-label,可鍵盤操作。 - 使用者系統開啟「減少動態效果」時,
respectReducedMotion預設會一併靜音,尊重降噪偏好;音效關閉時互動行為(onClick等)不受影響,只是不發聲。
常見問題
可以在 Next.js App Router 使用嗎?
可以。原始碼已有 "use client",SoundProvider 可以直接放在 Server Component 的版面裡。useSound()、SoundButton 與 SoundToggle 都必須在 SoundProvider 範圍內使用,否則 useSound() 會拋出錯誤。
為什麼頁面載入後第一次沒有聲音?
瀏覽器的自動播放政策要求音訊由使用者操作觸發。AudioContext 會在第一次呼叫 play() 時才建立並嘗試 resume,所以應該從點擊等事件裡呼叫,而不是在頁面載入時自動播放。
需要準備音檔嗎?
不需要。所有音效都是用 OscillatorNode 與 GainNode 即時合成,不載入音檔、不發額外請求,依賴只有 motion(偵測減少動態效果設定)與 lucide-react(開關圖示)。
使用者的偏好會被記住嗎?
會。開關與音量預設存在 localStorage 的 "wb-sound-kit" 鍵,可以用 storageKey 換成別的鍵,或傳 null 停用。
開啟減少動態效果時會發聲嗎?
預設不會。respectReducedMotion 預設為 true,系統開啟減少動態效果時 play() 不發聲,按鈕的 onClick 等行為照常執行;不需要這個行為時傳 respectReducedMotion={false}。