Get started
Sound and haptics
Opt-in clicks, detents and thunks, synthesised in the browser. Off unless you turn them on.
Real switches make a noise, and the noise tells you the switch went. Bevel controls can do the same: a tick for each detent on a knob, a snap for a toggle, a thunk when a breaker or a plug lands. It's off by default, and stays off until your app turns it on.
Off: the controls are silent.
Not listening
Turn it on
Install the feedback item (components that make sounds pull it in for you), then wrap the part of your app that should click in BevelFeedback. Drive it from a setting, so people who don't want sound never hear it.
npx shadcn@latest add @bevel/feedback"use client"
import { BevelFeedback } from "@/lib/feedback"
export function Providers({ children, settings }) {
return (
<BevelFeedback sound={settings.sound} haptics={settings.haptics}>
{children}
</BevelFeedback>
)
}Your own controls
useBevelFeedback() returns click, detent, thunk and toggle. Call them from event handlers. Outside the provider, or with it off, they do nothing, so a component can call them unconditionally.
const { click } = useBevelFeedback()
<button onClick={() => { click(); save() }}>Save</button>How it works
- Sounds are synthesised with the Web Audio API: short bursts of filtered noise, with a falling tone under the heavy ones. There are no audio files to load.
- The audio context is created on the first sound, which always comes from a click or a key press, so browsers let it start.
- Haptics use
navigator.vibrate, which Android browsers support. Elsewhere they do nothing. - Only actions make a sound. Nothing clicks when a value changes on its own.