@akshatbuilds/sonix 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +123 -0
  3. package/app/globals.css +110 -0
  4. package/app/layout.tsx +54 -0
  5. package/app/page.tsx +40 -0
  6. package/app/usage/page.tsx +9 -0
  7. package/cli/index.mjs +151 -0
  8. package/components/sound-card.tsx +595 -0
  9. package/components/sounds-gallery.tsx +137 -0
  10. package/components/theme-provider.tsx +11 -0
  11. package/components/theme-toggle.tsx +82 -0
  12. package/components/ui/button.tsx +57 -0
  13. package/components/ui/checkbox.tsx +30 -0
  14. package/components/ui/slider.tsx +28 -0
  15. package/components/ui/switch.tsx +29 -0
  16. package/components/ui/tooltip.tsx +30 -0
  17. package/components/usage-guide.tsx +155 -0
  18. package/components.json +21 -0
  19. package/lib/sounds.ts +329 -0
  20. package/lib/utils.ts +6 -0
  21. package/next-env.d.ts +6 -0
  22. package/next.config.mjs +5 -0
  23. package/package.json +96 -0
  24. package/postcss.config.mjs +8 -0
  25. package/public/click1.mp3 +0 -0
  26. package/public/click2.mp3 +0 -0
  27. package/public/registry/index.json +92 -0
  28. package/public/registry/sounds/button-click-secondary.json +13 -0
  29. package/public/registry/sounds/button-click.json +13 -0
  30. package/public/registry/sounds/error-beep.json +10 -0
  31. package/public/registry/sounds/error-buzz.json +10 -0
  32. package/public/registry/sounds/hover-blip.json +10 -0
  33. package/public/registry/sounds/hover-soft.json +10 -0
  34. package/public/registry/sounds/key-press.json +10 -0
  35. package/public/registry/sounds/notification-ping.json +10 -0
  36. package/public/registry/sounds/notification-subtle.json +10 -0
  37. package/public/registry/sounds/pop.json +10 -0
  38. package/public/registry/sounds/slider-tick.json +10 -0
  39. package/public/registry/sounds/success-bell.json +10 -0
  40. package/public/registry/sounds/success-chime.json +10 -0
  41. package/public/registry/sounds/swoosh.json +10 -0
  42. package/scripts/build-registry.mjs +293 -0
  43. package/tailwind.config.ts +100 -0
  44. package/tsconfig.json +33 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 abhi-yo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,123 @@
1
+ # Sonix — UI Sound Effects Library
2
+
3
+ Micro UX audio for interactive web applications. Zero dependencies. Copy-paste ready.
4
+
5
+ [Live Demo](https://abhi-yo.github.io/sound-library) · [Browse Sounds](#available-sounds) · [CLI](#cli)
6
+
7
+ ---
8
+
9
+ ## Features
10
+
11
+ - **14 sounds** — clicks, hovers, success, error, notifications, transitions, and more
12
+ - **Zero dependencies** — every snippet is standalone Web Audio API code
13
+ - **Copy & paste** — hit the copy button on any card to get production-ready code
14
+ - **CLI** — `npx sonix add pop` to scaffold sounds into your project
15
+ - **Framework agnostic** — works with React, Vue, Svelte, vanilla JS, anything
16
+ - **Dark mode** — with cinematic circular reveal transition
17
+ - **`prefers-reduced-motion`** — respects user accessibility preferences
18
+
19
+ ## Quick Start
20
+
21
+ ### Web Audio (no files needed)
22
+
23
+ ```js
24
+ const ctx = new AudioContext();
25
+ const osc = ctx.createOscillator();
26
+ const gain = ctx.createGain();
27
+ osc.type = 'sine';
28
+ osc.frequency.setValueAtTime(800, ctx.currentTime);
29
+ gain.gain.setValueAtTime(0.25, ctx.currentTime);
30
+ gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.15);
31
+ osc.connect(gain).connect(ctx.destination);
32
+ osc.start();
33
+ osc.stop(ctx.currentTime + 0.15);
34
+ ```
35
+
36
+ ### MP3 (for button clicks)
37
+
38
+ ```js
39
+ const audio = new Audio('/click1.mp3');
40
+ audio.volume = 0.5;
41
+ audio.play();
42
+ ```
43
+
44
+ ### React Hook
45
+
46
+ ```tsx
47
+ import { playSound } from './lib/sounds';
48
+
49
+ function MyButton() {
50
+ return <button onClick={() => playSound('pop')}>Click</button>;
51
+ }
52
+ ```
53
+
54
+ ## CLI
55
+
56
+ Add sounds to any project with the CLI:
57
+
58
+ ```bash
59
+ npx sonix list # List all available sounds
60
+ npx sonix add pop # Add a single sound
61
+ npx sonix add pop swoosh # Add multiple sounds
62
+ ```
63
+
64
+ Sounds are written to `lib/sounds/` as standalone files — no runtime dependency.
65
+
66
+ ## Available Sounds
67
+
68
+ | Name | Category | Source | Use Case |
69
+ |------|----------|--------|----------|
70
+ | `button-click` | Interaction | MP3 | Primary button presses |
71
+ | `button-click-secondary` | Interaction | MP3 | Secondary / ghost buttons |
72
+ | `hover-blip` | Feedback | Web Audio | Hovering over interactive elements |
73
+ | `hover-soft` | Feedback | Web Audio | Subtle hover feedback |
74
+ | `success-chime` | Notification | Web Audio | Form submissions, saves |
75
+ | `success-bell` | Notification | Web Audio | Achievements, milestones |
76
+ | `error-buzz` | Alert | Web Audio | Validation errors, failed actions |
77
+ | `error-beep` | Alert | Web Audio | Warnings, blocked actions |
78
+ | `notification-ping` | Alert | Web Audio | New messages, alerts |
79
+ | `notification-subtle` | Alert | Web Audio | Background updates |
80
+ | `swoosh` | Transition | Web Audio | Page transitions, panel slides |
81
+ | `pop` | Interaction | Web Audio | Tooltips, popovers, reveals |
82
+ | `slider-tick` | Interaction | Web Audio | Sliders, range inputs |
83
+ | `key-press` | Interaction | Web Audio | Text inputs, keyboards |
84
+
85
+ ## Accessibility
86
+
87
+ All sounds respect `prefers-reduced-motion: reduce`. When the user has requested reduced motion, sounds are automatically silenced.
88
+
89
+ To manually control sound:
90
+
91
+ ```js
92
+ // Check user preference
93
+ const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
94
+
95
+ if (!prefersReducedMotion) {
96
+ playSound('pop');
97
+ }
98
+ ```
99
+
100
+ ## Development
101
+
102
+ ```bash
103
+ pnpm install
104
+ pnpm dev
105
+ ```
106
+
107
+ ## Build
108
+
109
+ ```bash
110
+ pnpm build
111
+ ```
112
+
113
+ ## Registry
114
+
115
+ The project includes a shadcn-style registry at `public/registry/`. To rebuild:
116
+
117
+ ```bash
118
+ node scripts/build-registry.mjs
119
+ ```
120
+
121
+ ## License
122
+
123
+ MIT — see [LICENSE](./LICENSE).
@@ -0,0 +1,110 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ @layer utilities {
6
+ .text-balance {
7
+ text-wrap: balance;
8
+ }
9
+ }
10
+
11
+ @layer base {
12
+ :root {
13
+ --background: 0 0% 100%;
14
+ --foreground: 0 0% 3.9%;
15
+ --card: 0 0% 100%;
16
+ --card-foreground: 0 0% 3.9%;
17
+ --popover: 0 0% 100%;
18
+ --popover-foreground: 0 0% 3.9%;
19
+ --primary: 0 0% 9%;
20
+ --primary-foreground: 0 0% 98%;
21
+ --secondary: 0 0% 96.1%;
22
+ --secondary-foreground: 0 0% 9%;
23
+ --muted: 0 0% 96.1%;
24
+ --muted-foreground: 0 0% 45.1%;
25
+ --accent: 0 0% 96.1%;
26
+ --accent-foreground: 0 0% 9%;
27
+ --destructive: 0 84.2% 60.2%;
28
+ --destructive-foreground: 0 0% 98%;
29
+ --border: 0 0% 89.8%;
30
+ --input: 0 0% 89.8%;
31
+ --ring: 0 0% 3.9%;
32
+ --chart-1: 12 76% 61%;
33
+ --chart-2: 173 58% 39%;
34
+ --chart-3: 197 37% 24%;
35
+ --chart-4: 43 74% 66%;
36
+ --chart-5: 27 87% 67%;
37
+ --radius: 0.5rem;
38
+ --sidebar-background: 0 0% 98%;
39
+ --sidebar-foreground: 240 5.3% 26.1%;
40
+ --sidebar-primary: 240 5.9% 10%;
41
+ --sidebar-primary-foreground: 0 0% 98%;
42
+ --sidebar-accent: 240 4.8% 95.9%;
43
+ --sidebar-accent-foreground: 240 5.9% 10%;
44
+ --sidebar-border: 220 13% 91%;
45
+ --sidebar-ring: 217.2 91.2% 59.8%;
46
+ }
47
+ .dark {
48
+ --background: 0 0% 3.9%;
49
+ --foreground: 0 0% 98%;
50
+ --card: 0 0% 3.9%;
51
+ --card-foreground: 0 0% 98%;
52
+ --popover: 0 0% 3.9%;
53
+ --popover-foreground: 0 0% 98%;
54
+ --primary: 0 0% 98%;
55
+ --primary-foreground: 0 0% 9%;
56
+ --secondary: 0 0% 14.9%;
57
+ --secondary-foreground: 0 0% 98%;
58
+ --muted: 0 0% 14.9%;
59
+ --muted-foreground: 0 0% 63.9%;
60
+ --accent: 0 0% 14.9%;
61
+ --accent-foreground: 0 0% 98%;
62
+ --destructive: 0 62.8% 30.6%;
63
+ --destructive-foreground: 0 0% 98%;
64
+ --border: 0 0% 14.9%;
65
+ --input: 0 0% 14.9%;
66
+ --ring: 0 0% 83.1%;
67
+ --chart-1: 220 70% 50%;
68
+ --chart-2: 160 60% 45%;
69
+ --chart-3: 30 80% 55%;
70
+ --chart-4: 280 65% 60%;
71
+ --chart-5: 340 75% 55%;
72
+ --sidebar-background: 240 5.9% 10%;
73
+ --sidebar-foreground: 240 4.8% 95.9%;
74
+ --sidebar-primary: 224.3 76.3% 48%;
75
+ --sidebar-primary-foreground: 0 0% 100%;
76
+ --sidebar-accent: 240 3.7% 15.9%;
77
+ --sidebar-accent-foreground: 240 4.8% 95.9%;
78
+ --sidebar-border: 240 3.7% 15.9%;
79
+ --sidebar-ring: 217.2 91.2% 59.8%;
80
+ }
81
+ }
82
+
83
+ @layer base {
84
+ * {
85
+ @apply border-border;
86
+ }
87
+ body {
88
+ @apply bg-background text-foreground;
89
+ }
90
+ }
91
+
92
+ /* Circular reveal via View Transition API for cinematic theme change */
93
+ ::view-transition-old(root) {
94
+ animation: none;
95
+ z-index: 1;
96
+ }
97
+
98
+ ::view-transition-new(root) {
99
+ animation: none;
100
+ z-index: 9999;
101
+ mix-blend-mode: normal;
102
+ }
103
+
104
+ /* Honor prefers-reduced-motion (Web Interface Guidelines: Animation) */
105
+ @media (prefers-reduced-motion: reduce) {
106
+ ::view-transition-old(root),
107
+ ::view-transition-new(root) {
108
+ animation: none !important;
109
+ }
110
+ }
package/app/layout.tsx ADDED
@@ -0,0 +1,54 @@
1
+ import React from "react"
2
+ import type { Metadata } from 'next'
3
+ import { Geist, Geist_Mono } from 'next/font/google'
4
+ import { ThemeProvider } from '@/components/theme-provider'
5
+ import { Analytics } from '@vercel/analytics/next'
6
+
7
+ import './globals.css'
8
+
9
+ const geist = Geist({ subsets: ['latin'], variable: '--font-geist' })
10
+ const geistMono = Geist_Mono({ subsets: ['latin'], variable: '--font-geist-mono' })
11
+
12
+ export const metadata: Metadata = {
13
+ title: 'Sonix — UI Sound Effects Library',
14
+ description: 'Micro UX audio interactions for web apps. 14 sounds, zero dependencies, copy-paste ready.',
15
+ metadataBase: new URL('https://abhi-yo.github.io/sound-library'),
16
+ openGraph: {
17
+ title: 'Sonix — UI Sound Effects Library',
18
+ description: 'Micro UX audio interactions for web apps. 14 sounds, zero dependencies, copy-paste ready.',
19
+ type: 'website',
20
+ },
21
+ twitter: {
22
+ card: 'summary_large_image',
23
+ title: 'Sonix — UI Sound Effects Library',
24
+ description: 'Micro UX audio interactions for web apps. 14 sounds, zero dependencies, copy-paste ready.',
25
+ },
26
+ }
27
+
28
+ export default function RootLayout({
29
+ children,
30
+ }: Readonly<{
31
+ children: React.ReactNode
32
+ }>) {
33
+ return (
34
+ <html lang="en" suppressHydrationWarning className={`${geist.variable} ${geistMono.variable}`}>
35
+ <head>
36
+ <meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)" />
37
+ <meta name="theme-color" content="#0a0a0a" media="(prefers-color-scheme: dark)" />
38
+ </head>
39
+ <body className="font-sans antialiased">
40
+ <a href="#main-content" className="sr-only focus:not-sr-only focus:fixed focus:left-4 focus:top-4 focus:z-50 focus:rounded-md focus:bg-primary focus:px-4 focus:py-2 focus:text-primary-foreground">
41
+ Skip to main content
42
+ </a>
43
+ <ThemeProvider
44
+ attribute="class"
45
+ defaultTheme="system"
46
+ enableSystem
47
+ >
48
+ {children}
49
+ </ThemeProvider>
50
+ <Analytics />
51
+ </body>
52
+ </html>
53
+ )
54
+ }
package/app/page.tsx ADDED
@@ -0,0 +1,40 @@
1
+ import { SoundsGallery } from '@/components/sounds-gallery';
2
+ import { UsageGuide } from '@/components/usage-guide';
3
+ import { ThemeToggle } from '@/components/theme-toggle';
4
+
5
+ export const metadata = {
6
+ title: 'UI Sound Effects Library',
7
+ description: 'Tiny audio interactions for web apps. Button clicks, hover blips, success sounds, and error feedback.',
8
+ };
9
+
10
+ export default function Home() {
11
+ return (
12
+ <main id="main-content" className="flex min-h-screen flex-col bg-background text-foreground">
13
+ <div className="flex flex-col gap-12 px-4 py-12 sm:px-8 lg:px-16">
14
+ {/* Header */}
15
+ <div className="flex flex-col gap-4 border-b border-border pb-8">
16
+ <div className="flex items-center justify-between">
17
+ <h1 className="font-sans text-4xl font-bold tracking-tight sm:text-5xl">
18
+ Sound Effects
19
+ </h1>
20
+ <ThemeToggle />
21
+ </div>
22
+ <p className="max-w-2xl text-lg text-muted-foreground">
23
+ Micro UX audio for interactive web applications. Click to preview, copy code to use.
24
+ </p>
25
+ </div>
26
+
27
+ {/* Gallery */}
28
+ <SoundsGallery />
29
+
30
+ {/* Usage Guide */}
31
+ <UsageGuide />
32
+
33
+ {/* Footer */}
34
+ <div className="border-t border-border py-8 text-center text-sm text-muted-foreground">
35
+ <p>Click the play button to preview. Use the copy button to get code for your project.</p>
36
+ </div>
37
+ </div>
38
+ </main>
39
+ );
40
+ }
@@ -0,0 +1,9 @@
1
+ import { UsageGuide } from '@/components/usage-guide';
2
+
3
+ export default function UsagePage() {
4
+ return (
5
+ <div className="container mx-auto px-4 py-12">
6
+ <UsageGuide />
7
+ </div>
8
+ );
9
+ }
package/cli/index.mjs ADDED
@@ -0,0 +1,151 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * sonix CLI — add sounds to your project
5
+ *
6
+ * Usage:
7
+ * npx sonix add <sound-name> Add a single sound
8
+ * npx sonix add <s1> <s2> ... Add multiple sounds
9
+ * npx sonix list List all available sounds
10
+ *
11
+ * Sounds are fetched from the public registry and written
12
+ * to your project as standalone files — no runtime dependency.
13
+ */
14
+
15
+ import { writeFileSync, mkdirSync, existsSync } from "node:fs";
16
+ import { join } from "node:path";
17
+
18
+ const REGISTRY_URL =
19
+ process.env.SONIX_REGISTRY ||
20
+ "https://abhi-yo.github.io/sound-library/registry";
21
+
22
+ const COLORS = {
23
+ reset: "\x1b[0m",
24
+ green: "\x1b[32m",
25
+ cyan: "\x1b[36m",
26
+ yellow: "\x1b[33m",
27
+ red: "\x1b[31m",
28
+ dim: "\x1b[2m",
29
+ bold: "\x1b[1m",
30
+ };
31
+
32
+ function log(msg) {
33
+ console.log(msg);
34
+ }
35
+
36
+ function success(msg) {
37
+ log(`${COLORS.green}✓${COLORS.reset} ${msg}`);
38
+ }
39
+
40
+ function warn(msg) {
41
+ log(`${COLORS.yellow}⚠${COLORS.reset} ${msg}`);
42
+ }
43
+
44
+ function error(msg) {
45
+ log(`${COLORS.red}✗${COLORS.reset} ${msg}`);
46
+ }
47
+
48
+ async function fetchJSON(url) {
49
+ const res = await fetch(url);
50
+ if (!res.ok) throw new Error(`Failed to fetch ${url}: ${res.status}`);
51
+ return res.json();
52
+ }
53
+
54
+ async function listSounds() {
55
+ try {
56
+ const registry = await fetchJSON(`${REGISTRY_URL}/index.json`);
57
+ log("");
58
+ log(`${COLORS.bold}Available sounds (${registry.sounds.length}):${COLORS.reset}`);
59
+ log("");
60
+
61
+ const categories = {};
62
+ for (const s of registry.sounds) {
63
+ const cat = s.category || "other";
64
+ if (!categories[cat]) categories[cat] = [];
65
+ categories[cat].push(s);
66
+ }
67
+
68
+ for (const [cat, sounds] of Object.entries(categories)) {
69
+ log(` ${COLORS.cyan}${cat}${COLORS.reset}`);
70
+ for (const s of sounds) {
71
+ const badge = s.type === "mp3" ? `${COLORS.yellow}mp3${COLORS.reset}` : `${COLORS.dim}web audio${COLORS.reset}`;
72
+ log(` ${s.name.padEnd(24)} ${badge} ${COLORS.dim}${s.description}${COLORS.reset}`);
73
+ }
74
+ log("");
75
+ }
76
+ } catch (err) {
77
+ error(`Could not fetch registry: ${err.message}`);
78
+ process.exit(1);
79
+ }
80
+ }
81
+
82
+ async function addSounds(names) {
83
+ const outDir = join(process.cwd(), "lib", "sounds");
84
+
85
+ if (!existsSync(outDir)) {
86
+ mkdirSync(outDir, { recursive: true });
87
+ success(`Created ${COLORS.cyan}lib/sounds/${COLORS.reset}`);
88
+ }
89
+
90
+ for (const name of names) {
91
+ try {
92
+ const sound = await fetchJSON(`${REGISTRY_URL}/sounds/${name}.json`);
93
+
94
+ for (const file of sound.files) {
95
+ const filePath = join(outDir, file.name);
96
+ writeFileSync(filePath, file.content + "\n");
97
+ success(`Added ${COLORS.cyan}lib/sounds/${file.name}${COLORS.reset}`);
98
+ }
99
+
100
+ if (sound.assets?.length) {
101
+ warn(
102
+ `${name} requires asset files: ${sound.assets.join(", ")} — download them from the website and put in your public/ folder.`
103
+ );
104
+ }
105
+ } catch (err) {
106
+ error(`Could not add "${name}": ${err.message}`);
107
+ }
108
+ }
109
+
110
+ log("");
111
+ log(`${COLORS.dim}Import and use:${COLORS.reset}`);
112
+ log(`${COLORS.dim} import { play${toPascal(names[0])} } from './lib/sounds/${names[0]}';${COLORS.reset}`);
113
+ log("");
114
+ }
115
+
116
+ function toPascal(str) {
117
+ return str
118
+ .split("-")
119
+ .map((w) => w[0].toUpperCase() + w.slice(1))
120
+ .join("");
121
+ }
122
+
123
+ // --- Main ---
124
+
125
+ const args = process.argv.slice(2);
126
+ const command = args[0];
127
+
128
+ if (!command || command === "help" || command === "--help") {
129
+ log("");
130
+ log(`${COLORS.bold}sonix${COLORS.reset} — UI sound effects for your app`);
131
+ log("");
132
+ log(` ${COLORS.cyan}npx sonix list${COLORS.reset} List all sounds`);
133
+ log(` ${COLORS.cyan}npx sonix add <name>${COLORS.reset} Add a sound to your project`);
134
+ log(` ${COLORS.cyan}npx sonix add pop swoosh${COLORS.reset} Add multiple sounds`);
135
+ log("");
136
+ process.exit(0);
137
+ }
138
+
139
+ if (command === "list") {
140
+ await listSounds();
141
+ } else if (command === "add") {
142
+ const names = args.slice(1);
143
+ if (names.length === 0) {
144
+ error("Specify at least one sound name. Run `npx sonix list` to see options.");
145
+ process.exit(1);
146
+ }
147
+ await addSounds(names);
148
+ } else {
149
+ error(`Unknown command: ${command}. Run \`npx sonix --help\`.`);
150
+ process.exit(1);
151
+ }