# Ding
> Spring-physics toast notifications for React, Svelte 5 and plain DOM. Zero dependencies. Styles are injected from JS, so there is no stylesheet to import. Human docs: /docs.html. Release notes with live demos: /changelog/
## Install
- React: `npm i @yusufceng1z/ding-react`
- Svelte 5: `npm i @yusufceng1z/ding-svelte`
- Vanilla: `npm i @yusufceng1z/ding`
- shadcn/ui (React): `npx shadcn@latest add @ding/ding`
## Setup
Render the toaster once, near the root.
- React: `import { Toaster, toast } from '@yusufceng1z/ding-react'` then ``
- Svelte 5: `import { Toaster, toast } from '@yusufceng1z/ding-svelte'` then ``
- Vanilla: `import { mountToaster, toast } from '@yusufceng1z/ding'` then `const unmount = mountToaster({ position, theme })`
## API
All calls return the toast id. Import `toast` from the package.
- `toast.success(title, options?)`, `toast.error`, `toast.warning`, `toast.info`, `toast.loading`
- `toast.promise(promise, { loading, success, error })` morphs one toast from loading to the result
- `toast.update(id, patch)` changes a live toast in place. A patch may set title, description, type, duration, progress, action, secondary, meta, image. `null` removes action, secondary, meta, image and progress.
- `toast.dismiss(id?)` closes one toast, or all
- `toast.configure({ position, theme, labels, sound, volume })`. `labels: { region, dismiss }` sets the accessible names of the region and the close button (English by default)
Options: `description`, `action { label, onClick }`, `secondary { label, onClick }`, `image` (avatar URL), `meta` (small trailing text), `progress` (0 to 1), `duration` (ms, 0 stays until dismissed), `id`.
`onClick` may return a patch. The toast then morphs in place instead of closing. A returned patch removes the buttons unless it names them again.
Positions: top-left, top-center, top-right, bottom-left, bottom-center, bottom-right. Themes: system, light, dark.
## Sound
Off by default. `toast.configure({ sound: true, volume: 0.5 })` plays a soft built-in mallet tone per status, no audio files (success rises, error falls, warning double-taps, info is one note). Volume is 0 to 1. Loading is silent. Browsers keep audio locked until a click or key press, so the first sound must follow one.
To use another sound library, pass a function: `toast.configure({ sound: (type) => play(type) })`. It is called with `success`, `error`, `warning`, `info` or `loading`, once per new toast and once per status change. Progress updates make no sound.
## Behavior
- One toast is shown at a time. A new call morphs the live toast instead of stacking. Only the newest caller can update it, so a late result from an older call is ignored.
- Repeating an identical toast adds a counter (×2) instead of a new toast.
- Errors, loading and toasts with an action stay until dismissed. Other toasts close after 5 seconds. Hover and keyboard focus pause the timer.
- Escape dismisses. Alt+T moves focus into the toast.
- Messages go through persistent live regions: errors assertive, everything else polite.
- Respects prefers-reduced-motion. Mirrors in right-to-left pages.