YusufCeng1z/ding has 599 GitHub stars and 16 forks. GitHot measured +74 stars in 24 hours. Last checked 2026-11-12 14:48 UTC. Last push 2026-11-06.
采用真实弹簧物理效果的通知。单条通知可原地变形。无依赖。支持 React、Svelte 和原生 DOM。
实测星标增长
- 24小时
- +74
- 7天
- 更新延迟
- 15天
- 更新延迟
- 1个月
- 更新延迟
各周期内的星标净变化,周期相互重叠,并非每日新增。缺失的测量保持空白。星标代表关注度,不代表软件质量。
README.md
原文
Ding
Toasts with real spring physics.
One toast that morphs in place. No dependencies. React, Svelte or plain DOM.
Why Ding
- One toast at a time. A new call morphs the toast on screen instead of stacking a second one.
- Real springs. Size, shape and text move on a spring solver, so interrupting an animation keeps its momentum.
- No stylesheet. Styles are injected from JavaScript. Nothing to import.
- Light, dark and system themes, six positions, right-to-left support.
- Rich content. Description, avatar, buttons, progress bar, a countdown line, small trailing text.
- Accessible by default. Persistent live regions, keyboard dismiss,
prefers-reduced-motion. - Optional sound. A soft built-in tone per status, or plug in your own.
- Strict TypeScript, ESM and CommonJS builds.
Install
npm i @yusufceng1z/ding-react # React 18+
npm i @yusufceng1z/ding-svelte # Svelte 5
npm i @yusufceng1z/ding # plain DOM
Each framework package depends on the small @yusufceng1z/ding core, so you install only one.
Quick start
Render the toaster once, then call toast from anywhere.
React
import { Toaster, toast } from '@yusufceng1z/ding-react';
export default function App() {
return (
<>
<Toaster position="top-center" theme="system" />
<button onClick={() => toast.success('Saved')}>Save</button>
</>
);
}
Svelte 5
<script>
import { Toaster, toast } from '@yusufceng1z/ding-svelte';
</script>
<Toaster position="top-center" theme="system" />
<button onclick={() => toast.success('Saved')}>Save</button>
Plain DOM
import { mountToaster, toast } from '@yusufceng1z/ding';
mountToaster({ position: 'top-center' });
toast.success('Saved');
Usage
toast.success('Saved');
toast.error('Payment failed', { description: 'Card declined' });
toast.warning('Storage almost full');
toast.info('New version available');
toast.loading('Uploading…');
Every call returns the toast id.
Promises and updates
toast.promise(save(), {
loading: 'Saving…',
success: 'Saved',
error: (err) => `Could not save: ${err.message}`,
});
const id = toast.loading('Uploading photo…', { progress: 0 });
toast.update(id, { progress: 0.6 });
toast.update(id, { type: 'success', title: 'Photo uploaded', progress: null });
Only the newest caller can update the shared toast, so a slow request that finishes late cannot overwrite a newer message.
Actions
An onClick may return a patch. The toast then morphs in place instead of closing.
toast.success('Message deleted', {
action: {
label: 'Undo',
onClick: () => {
restore();
return { type: 'info', title: 'Restored' };
},
},
});
Options
| Option | Description |
|---|---|
description |
Second line of text |
action |
{ label, onClick }, the main button |
secondary |
{ label, onClick }, a quieter button beside the action |
image |
Avatar or thumbnail URL, replaces the status icon |
meta |
Small trailing text, such as now or ⌘V |
progress |
0 to 1, draws a thin bar at the bottom |
duration |
Milliseconds before it closes. 0 keeps it until dismissed. Default 5000 |
id |
Reuse an id so repeated calls from one place stay one caller |
Errors, loading toasts and toasts with a button stay until dismissed. Hover and keyboard focus pause the timer. Repeating the same toast adds a counter (×2).
Configure
toast.configure({
position: 'bottom-right', // top-left, top-center, top-right, bottom-left, bottom-center, bottom-right
theme: 'dark', // system, light, dark
sound: true,
volume: 0.5,
});
toast.dismiss(id); // one toast
toast.dismiss(); // all
Sound
Off by default. With sound: true each status plays its own soft tone: success rises, error falls, warning double-taps, info is a single note. Loading is silent and progress updates never make a sound. Browsers keep audio locked until a click or key press.
To use your own sounds, pass a function. It receives success, error, warning, info or loading:
toast.configure({ sound: (type) => mySounds.play(type) });
Accessibility
- Messages go through persistent live regions: errors are assertive, everything else is polite.
Escdismisses.Alt+Tmoves focus into the toast.- Respects
prefers-reduced-motion. - Mirrors automatically on right-to-left pages.
Packages
| Package | Description |
|---|---|
@yusufceng1z/ding |
Springs, timers, store, sound and the plain DOM renderer |
@yusufceng1z/ding-react |
React renderer |
@yusufceng1z/ding-svelte |
Svelte 5 renderer |
The core owns all behavior. The framework packages only render.
Development
pnpm install
pnpm test # unit tests
pnpm typecheck
pnpm build # build all packages
pnpm dev # docs and demo site
The docs site lives in apps/docs. A machine-readable summary is in apps/docs/public/llms.txt.