الأصوات والاهتزازات
24 صوت واجهة تُركَّب في المتصفح لحظة تشغيلها: بلا ملفات صوتية، لا شيء تستضيفه، وحدة واحدة بحجم 7 KB. كل صوت وصفة صغيرة من جزئيتين أو ثلاث جزئيات جيبية على سلم دو الكبير الخماسي، وهجمة ناعمة ونقرة مرشَّحة، مُمزَّجة عند −14 dBFS. الأصوات متوقفة حتى يشغّلها الزائر، وتُحفظ لكل متصفح، ولا تُشغَّل أبدًا قبل إيماءة من المستخدم. الحركة والصوت تفضيلان منفصلان: prefers-reduced-motion لا يغيّرهما.
data-sound فلا تصدر صوتًا إلا عند التفعيل.التثبيت
npx shadcn@latest add https://goodui.dev/r/sounds.jsonيصل إلى lib/goodui-sounds.ts. المكونات التي لها خاصية sound تدرجه كتبعية، فيأتي معها. تُحمَّل الوحدة بكسل وفقط حين يكون sound مفعّلًا في مكوّن ما، فالتطبيق الذي لا يستخدمه لا يشحن منه شيئًا.
كل الأصوات
انقر على بطاقة لتسمعها. الموجة هي الصوت مُصيَّرًا دون اتصال، ومعاير بحيث تبقى الأصوات الهادئة مقروءة.
المكونات
مرّر sound لتشغيل الصوت الافتراضي للمكوّن، أو اسم صوت لاختيار غيره. كل شيء متوقف ما لم تمرّره.
button: tap عند النقرstateful-button: tap، ثم success أو error حسب النتيجةswitch: toggle-on / toggle-offcheckbox: pop عند التحديد، tick عند إلغائهtabs: tick عند الاختيارrating: tick لكل نجمة تحت المؤشر، يرتفع طبقةً على طول الصف، وpop عند الاختيارnumber-input: tick لكل خطوة، أعلى لـ + وأخفض لـ −dialog: open / closetoast-stack (Toaster): success أو error أو warning أو notify لكل إشعار جديد، ومجددًا حين يُحسم إشعار تحميلconfetti: celebrate مع الانفجار (fireConfetti({ sound: true }) أو <Confetti sound />)hold-to-confirm: tick عند بدء الضغط المطوّل، وconfirm عند اكتمالهcopy-button: pop بعد النسخ، وerror عند فشله
<Button sound onClick={save}>Save</Button>
<Switch sound label="SMS reminders" />
<Rating sound defaultValue={4} />
<Dialog sound title="Cancel this booking?" onConfirm={cancel} trigger={<Button variant="outline">Cancel</Button>} />
<Toaster sound /> {/* success, error, warning or notify per toast */}
<HoldToConfirm sound="unlock" onConfirm={deleteAccount}>Delete account</HoldToConfirm>أعطِ الناس المفتاح
ركّب SoundProvider مرة واحدة وضع مفتاح تبديل في إعداداتك أو ترويستك. يُحفظ اختيار الزائر في localStorage ويغلب على الإعداد الافتراضي للتطبيق.
import { SoundProvider } from "@/lib/goodui-sounds"
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<SoundProvider>{children}</SoundProvider> {/* enabled: app default, persist, volume, attributes */}
</body>
</html>
)
}"use client"
import { useSound } from "@/lib/goodui-sounds"
export function SoundToggle() {
const { enabled, toggle, play } = useSound()
return (
<button type="button" role="switch" aria-checked={enabled} onClick={() => { toggle(); play(enabled ? "toggle-off" : "toggle-on", { force: true }) }}>
Sounds {enabled ? "on" : "off"}
</button>
)
}أي عنصر
مع تركيب provider، يستطيع أي عنصر تشغيل صوت بلا كود. data-sound يُشغَّل عند النقر (toggle يقرأ حالة التحديد بعد النقر)، وdata-sound-hover عند دخول المؤشر.
<button data-sound="tap">Book</button>
<input type="checkbox" data-sound="toggle" />
<a href="/pricing" data-sound-hover>Pricing</a> {/* "hover", very subtle */}API
import { play, useSound, setSoundEnabled, isSoundEnabled, setSoundVolume, SOUND_NAMES, type SoundName } from "@/lib/goodui-sounds"
play("success") // false when sounds are off, before the first gesture, or without Web Audio
play("tick", { rate: 1.1, volume: 0.6 }) // rate: pitch and speed, volume: 0..1 on top of the -14 dBFS bus
play("notify", { force: true }) // previews and settings: ignores the toggle
setSoundEnabled(true) // saved in localStorage (unless <SoundProvider persist={false} />)
setSoundVolume(0.5)
const { enabled, toggle, setEnabled, play } = useSound() // reads "off" during SSRصمّم صوتك
الوصفة قائمة أصوات: جزئيات مذبذب أو ضجيج مرشَّح، لكل منها بداية ومدة ومستوى وانزلاق اختياري. tone() يعطي نغمة مع جزئيتها الأوكتافية، وnoise() نقرة أو هبّة. أبقه قصيرًا (أقل من 350 ms)، وهادئًا (مستويات أقل من 0.5)، وعلى السلم، فيجلس إلى جانب الأصوات المدمجة.
import { defineSound, tone, noise, play } from "@/lib/goodui-sounds"
// A cash-register "cha-ching": a click, then two notes a fifth apart with a bright octave.
defineSound("cha-ching", [
noise(0.02, { gain: 0.2, filter: 3000 }),
...tone(1046.5, 0.12, { at: 0.03, gain: 0.4, shine: 0.3 }), // C6
...tone(1568, 0.26, { at: 0.11, gain: 0.42, shine: 0.3 }), // G6
])
play("cha-ching") // or <button data-sound="cha-ching">
defineSound("tap", [...]) // built-ins can be replaced the same wayحقول الصوت: wave (sine أو triangle أو square أو sawtooth أو noise)، freq وto بالهرتز، glide، at وdur وattack بالثواني، gain من 0 إلى 1، filter / filterTo / q لمرشح التمرير المنخفض (المذبذبات) أو تمرير النطاق (الضجيج)، detune بالسنت.
الاختبار
renderSound(name) يصيّر وصفة عبر OfflineAudioContext ويعيد المخزن المؤقت، فيستطيع اختبار ما أن يتحقق من أن الصوت ليس صامتًا ولا مقصوصًا دون تشغيل أي شيء.
const buffer = await renderSound("success")
const data = buffer.getChannelData(0)
const peak = Math.max(...data.map(Math.abs)) // the built-ins stay under 0.3 at volume 1goodui Native: الاهتزازات
الأسماء نفسها أنماط اهتزاز في goodui Native (foundation/haptics.ts)، مبنية من نقرات impact وnotification وselection في expo-haptics مع توقيت. cue(name) من foundation/sounds يشغّل النمط والصوت في استدعاء واحد، فالشاشة المكتوبة مرة واحدة تشعر بها صحيحة على كل منصة. الأصوات متوقفة حتى يستدعي التطبيق setSoundsEnabled(true)؛ على iOS وAndroid تُشغَّل عبر مشغّل يسجّله التطبيق بـ setSoundPlayer (expo-audio بملفاته القصيرة الخاصة)، وعلى Expo web تُركَّب كما هنا.
tap: impact خفيفselect, tick, count-tick: selectiontoggle-on / toggle-off: impact خفيف → متوسط / متوسط → خفيف، بفاصل 60 mssuccess, warning, error: notification من النوع نفسهnotify: نبضتا impact خفيفتان، بفاصل 90 mssend / receive: خفيف → صلب / ناعم → خفيفdelete: impact ثقيلundo: خفيف → متوسطpop: impact صلبwhoosh / slide: ثلاث / اثنتان من selectionconfirm: impact متوسط، ثم notification نجاحopen / close: ناعم → خفيف / خفيف → ناعمdrag-start / drop: impact متوسط / ثقيل → خفيفcelebrate: خفيف، خفيف، متوسط، ثم notification نجاحunlock: صلب، صلب، ثم notification نجاحhover: لا شيء (لا تحويم على اللمس)
import { cue, setSoundsEnabled } from "@/components/goodui-native/foundation"
setSoundsEnabled(settings.sounds) // from your settings screen
cue("confirm") // haptic pattern + sound, one vocabulary