@akshatbuilds/sonix 1.0.2 → 1.0.4
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.
- package/README.md +7 -6
- package/cli/index.mjs +82 -17
- package/package.json +9 -38
- package/app/globals.css +0 -110
- package/app/layout.tsx +0 -54
- package/app/page.tsx +0 -40
- package/app/usage/page.tsx +0 -9
- package/components/sound-card.tsx +0 -595
- package/components/sounds-gallery.tsx +0 -137
- package/components/theme-provider.tsx +0 -11
- package/components/theme-toggle.tsx +0 -82
- package/components/ui/button.tsx +0 -57
- package/components/ui/checkbox.tsx +0 -30
- package/components/ui/slider.tsx +0 -28
- package/components/ui/switch.tsx +0 -29
- package/components/ui/tooltip.tsx +0 -30
- package/components/usage-guide.tsx +0 -155
- package/components.json +0 -21
- package/lib/sounds.ts +0 -329
- package/lib/utils.ts +0 -6
- package/next-env.d.ts +0 -6
- package/next.config.mjs +0 -5
- package/postcss.config.mjs +0 -8
- package/public/click1.mp3 +0 -0
- package/public/click2.mp3 +0 -0
- package/public/registry/index.json +0 -92
- package/public/registry/sounds/button-click-secondary.json +0 -13
- package/public/registry/sounds/button-click.json +0 -13
- package/public/registry/sounds/error-beep.json +0 -10
- package/public/registry/sounds/error-buzz.json +0 -10
- package/public/registry/sounds/hover-blip.json +0 -10
- package/public/registry/sounds/hover-soft.json +0 -10
- package/public/registry/sounds/key-press.json +0 -10
- package/public/registry/sounds/notification-ping.json +0 -10
- package/public/registry/sounds/notification-subtle.json +0 -10
- package/public/registry/sounds/pop.json +0 -10
- package/public/registry/sounds/slider-tick.json +0 -10
- package/public/registry/sounds/success-bell.json +0 -10
- package/public/registry/sounds/success-chime.json +0 -10
- package/public/registry/sounds/swoosh.json +0 -10
- package/scripts/build-registry.mjs +0 -293
- package/tailwind.config.ts +0 -100
- package/tsconfig.json +0 -33
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ Micro UX audio for interactive web applications. Zero dependencies. Copy-paste r
|
|
|
11
11
|
- **14 sounds** — clicks, hovers, success, error, notifications, transitions, and more
|
|
12
12
|
- **Zero dependencies** — every snippet is standalone Web Audio API code
|
|
13
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
|
|
14
|
+
- **CLI** — `npx @akshatbuilds/sonix add pop` to scaffold sounds into your project
|
|
15
15
|
- **Framework agnostic** — works with React, Vue, Svelte, vanilla JS, anything
|
|
16
16
|
- **Dark mode** — with cinematic circular reveal transition
|
|
17
17
|
- **`prefers-reduced-motion`** — respects user accessibility preferences
|
|
@@ -36,7 +36,7 @@ osc.stop(ctx.currentTime + 0.15);
|
|
|
36
36
|
### MP3 (for button clicks)
|
|
37
37
|
|
|
38
38
|
```js
|
|
39
|
-
const audio = new Audio('/click1.mp3');
|
|
39
|
+
const audio = new Audio('/sonix/click1.mp3');
|
|
40
40
|
audio.volume = 0.5;
|
|
41
41
|
audio.play();
|
|
42
42
|
```
|
|
@@ -44,10 +44,10 @@ audio.play();
|
|
|
44
44
|
### React Hook
|
|
45
45
|
|
|
46
46
|
```tsx
|
|
47
|
-
import {
|
|
47
|
+
import { playPop } from '@/components/sonix';
|
|
48
48
|
|
|
49
49
|
function MyButton() {
|
|
50
|
-
return <button onClick={
|
|
50
|
+
return <button onClick={playPop}>Click</button>;
|
|
51
51
|
}
|
|
52
52
|
```
|
|
53
53
|
|
|
@@ -61,7 +61,8 @@ npx @akshatbuilds/sonix add pop # Add a single sound
|
|
|
61
61
|
npx @akshatbuilds/sonix add pop swoosh # Add multiple sounds
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
-
Sounds are written to `
|
|
64
|
+
Sounds are written to `components/sonix/` as standalone files — no runtime dependency.
|
|
65
|
+
MP3 assets are downloaded automatically to `public/sonix/`.
|
|
65
66
|
|
|
66
67
|
## Available Sounds
|
|
67
68
|
|
|
@@ -93,7 +94,7 @@ To manually control sound:
|
|
|
93
94
|
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
94
95
|
|
|
95
96
|
if (!prefersReducedMotion) {
|
|
96
|
-
|
|
97
|
+
playPop();
|
|
97
98
|
}
|
|
98
99
|
```
|
|
99
100
|
|
package/cli/index.mjs
CHANGED
|
@@ -4,20 +4,23 @@
|
|
|
4
4
|
* sonix CLI — add sounds to your project
|
|
5
5
|
*
|
|
6
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
|
|
7
|
+
* npx @akshatbuilds/sonix add <sound-name> Add a single sound
|
|
8
|
+
* npx @akshatbuilds/sonix add <s1> <s2> ... Add multiple sounds
|
|
9
|
+
* npx @akshatbuilds/sonix list List all available sounds
|
|
10
10
|
*
|
|
11
11
|
* Sounds are fetched from the public registry and written
|
|
12
12
|
* to your project as standalone files — no runtime dependency.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import { writeFileSync, mkdirSync, existsSync } from "node:fs";
|
|
16
|
-
import { join } from "node:path";
|
|
15
|
+
import { writeFileSync, mkdirSync, existsSync, readdirSync } from "node:fs";
|
|
16
|
+
import { join, dirname, relative } from "node:path";
|
|
17
17
|
|
|
18
18
|
const REGISTRY_URL =
|
|
19
19
|
process.env.SONIX_REGISTRY ||
|
|
20
20
|
"https://soundcomponents.vercel.app/registry";
|
|
21
|
+
const DEFAULT_OUT_DIR = process.env.SONIX_OUT_DIR || "components";
|
|
22
|
+
const SOUNDS_SUBDIR = "sonix";
|
|
23
|
+
const ASSETS_SUBDIR = "sonix";
|
|
21
24
|
|
|
22
25
|
const COLORS = {
|
|
23
26
|
reset: "\x1b[0m",
|
|
@@ -51,6 +54,16 @@ async function fetchJSON(url) {
|
|
|
51
54
|
return res.json();
|
|
52
55
|
}
|
|
53
56
|
|
|
57
|
+
async function fetchBinary(url) {
|
|
58
|
+
const res = await fetch(url);
|
|
59
|
+
if (!res.ok) throw new Error(`Failed to fetch ${url}: ${res.status}`);
|
|
60
|
+
return Buffer.from(await res.arrayBuffer());
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function getRegistryHost() {
|
|
64
|
+
return REGISTRY_URL.replace(/\/registry\/?$/, "");
|
|
65
|
+
}
|
|
66
|
+
|
|
54
67
|
async function listSounds() {
|
|
55
68
|
try {
|
|
56
69
|
const registry = await fetchJSON(`${REGISTRY_URL}/index.json`);
|
|
@@ -80,11 +93,16 @@ async function listSounds() {
|
|
|
80
93
|
}
|
|
81
94
|
|
|
82
95
|
async function addSounds(names) {
|
|
83
|
-
const outDir = join(process.cwd(),
|
|
96
|
+
const outDir = join(process.cwd(), DEFAULT_OUT_DIR, SOUNDS_SUBDIR);
|
|
97
|
+
const assetsDir = join(process.cwd(), "public", ASSETS_SUBDIR);
|
|
84
98
|
|
|
85
99
|
if (!existsSync(outDir)) {
|
|
86
100
|
mkdirSync(outDir, { recursive: true });
|
|
87
|
-
success(`Created ${COLORS.cyan}
|
|
101
|
+
success(`Created ${COLORS.cyan}${DEFAULT_OUT_DIR}/${SOUNDS_SUBDIR}/${COLORS.reset}`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (!existsSync(assetsDir)) {
|
|
105
|
+
mkdirSync(assetsDir, { recursive: true });
|
|
88
106
|
}
|
|
89
107
|
|
|
90
108
|
for (const name of names) {
|
|
@@ -93,26 +111,73 @@ async function addSounds(names) {
|
|
|
93
111
|
|
|
94
112
|
for (const file of sound.files) {
|
|
95
113
|
const filePath = join(outDir, file.name);
|
|
114
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
96
115
|
writeFileSync(filePath, file.content + "\n");
|
|
97
|
-
success(
|
|
116
|
+
success(
|
|
117
|
+
`Added ${COLORS.cyan}${DEFAULT_OUT_DIR}/${SOUNDS_SUBDIR}/${file.name}${COLORS.reset}`
|
|
118
|
+
);
|
|
98
119
|
}
|
|
99
120
|
|
|
100
121
|
if (sound.assets?.length) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
122
|
+
for (const assetName of sound.assets) {
|
|
123
|
+
const assetUrl = `${getRegistryHost()}/${assetName}`;
|
|
124
|
+
try {
|
|
125
|
+
const data = await fetchBinary(assetUrl);
|
|
126
|
+
writeFileSync(join(assetsDir, assetName), data);
|
|
127
|
+
success(`Added ${COLORS.cyan}public/${ASSETS_SUBDIR}/${assetName}${COLORS.reset}`);
|
|
128
|
+
} catch (assetErr) {
|
|
129
|
+
warn(`Could not fetch ${assetName} (${assetErr.message}). Download it manually to public/${ASSETS_SUBDIR}/.`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
104
132
|
}
|
|
105
133
|
} catch (err) {
|
|
106
134
|
error(`Could not add "${name}": ${err.message}`);
|
|
107
135
|
}
|
|
108
136
|
}
|
|
109
137
|
|
|
138
|
+
const soundFiles = collectSoundFiles(outDir);
|
|
139
|
+
const uniqueExports = new Map();
|
|
140
|
+
for (const filePath of soundFiles) {
|
|
141
|
+
const importPath = relative(outDir, filePath).replace(/\\/g, "/").replace(/\.ts$/, "");
|
|
142
|
+
const base = importPath.split("/").pop() || "";
|
|
143
|
+
uniqueExports.set(`play${toPascal(base)}`, { exportName: `play${toPascal(base)}`, importPath });
|
|
144
|
+
}
|
|
145
|
+
const indexPath = join(outDir, "index.ts");
|
|
146
|
+
const indexContent = Array.from(uniqueExports.values())
|
|
147
|
+
.sort((a, b) => a.exportName.localeCompare(b.exportName))
|
|
148
|
+
.map((item) => `export { ${item.exportName} } from "./${item.importPath}";`)
|
|
149
|
+
.join("\n");
|
|
150
|
+
if (indexContent) {
|
|
151
|
+
writeFileSync(indexPath, `${indexContent}\n`);
|
|
152
|
+
success(`Added ${COLORS.cyan}${DEFAULT_OUT_DIR}/${SOUNDS_SUBDIR}/index.ts${COLORS.reset}`);
|
|
153
|
+
}
|
|
154
|
+
|
|
110
155
|
log("");
|
|
111
156
|
log(`${COLORS.dim}Import and use:${COLORS.reset}`);
|
|
112
|
-
log(
|
|
157
|
+
log(
|
|
158
|
+
`${COLORS.dim} import { play${toPascal(names[0])} } from '@/components/sonix';${COLORS.reset}`
|
|
159
|
+
);
|
|
160
|
+
log(
|
|
161
|
+
`${COLORS.dim} // If you don't use @ alias: ../../components/sonix${COLORS.reset}`
|
|
162
|
+
);
|
|
113
163
|
log("");
|
|
114
164
|
}
|
|
115
165
|
|
|
166
|
+
function collectSoundFiles(dir) {
|
|
167
|
+
const files = [];
|
|
168
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
169
|
+
const full = join(dir, entry.name);
|
|
170
|
+
if (entry.isDirectory()) {
|
|
171
|
+
files.push(...collectSoundFiles(full));
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
if (entry.isFile() && entry.name.endsWith(".ts") && entry.name !== "index.ts") {
|
|
175
|
+
files.push(full);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return files;
|
|
179
|
+
}
|
|
180
|
+
|
|
116
181
|
function toPascal(str) {
|
|
117
182
|
return str
|
|
118
183
|
.split("-")
|
|
@@ -129,9 +194,9 @@ if (!command || command === "help" || command === "--help") {
|
|
|
129
194
|
log("");
|
|
130
195
|
log(`${COLORS.bold}sonix${COLORS.reset} — UI sound effects for your app`);
|
|
131
196
|
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`);
|
|
197
|
+
log(` ${COLORS.cyan}npx @akshatbuilds/sonix list${COLORS.reset} List all sounds`);
|
|
198
|
+
log(` ${COLORS.cyan}npx @akshatbuilds/sonix add <name>${COLORS.reset} Add a sound to your project`);
|
|
199
|
+
log(` ${COLORS.cyan}npx @akshatbuilds/sonix add pop swoosh${COLORS.reset} Add multiple sounds`);
|
|
135
200
|
log("");
|
|
136
201
|
process.exit(0);
|
|
137
202
|
}
|
|
@@ -141,11 +206,11 @@ if (command === "list") {
|
|
|
141
206
|
} else if (command === "add") {
|
|
142
207
|
const names = args.slice(1);
|
|
143
208
|
if (names.length === 0) {
|
|
144
|
-
error("Specify at least one sound name. Run `npx sonix list` to see options.");
|
|
209
|
+
error("Specify at least one sound name. Run `npx @akshatbuilds/sonix list` to see options.");
|
|
145
210
|
process.exit(1);
|
|
146
211
|
}
|
|
147
212
|
await addSounds(names);
|
|
148
213
|
} else {
|
|
149
|
-
error(`Unknown command: ${command}. Run \`npx sonix --help\`.`);
|
|
214
|
+
error(`Unknown command: ${command}. Run \`npx @akshatbuilds/sonix --help\`.`);
|
|
150
215
|
process.exit(1);
|
|
151
216
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akshatbuilds/sonix",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Micro UX sound effects for interactive web applications. Zero dependencies, copy-paste ready.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -20,63 +20,34 @@
|
|
|
20
20
|
"bin": {
|
|
21
21
|
"@akshatbuilds/sonix": "./cli/index.mjs"
|
|
22
22
|
},
|
|
23
|
+
"files": [
|
|
24
|
+
"cli",
|
|
25
|
+
"README.md",
|
|
26
|
+
"LICENSE"
|
|
27
|
+
],
|
|
23
28
|
"scripts": {
|
|
24
29
|
"dev": "next dev --turbo",
|
|
25
30
|
"build": "next build",
|
|
26
31
|
"start": "next start",
|
|
27
|
-
"lint": "next lint"
|
|
32
|
+
"lint": "next lint",
|
|
33
|
+
"build:registry": "node scripts/build-registry.mjs"
|
|
28
34
|
},
|
|
29
35
|
"dependencies": {
|
|
30
|
-
"@hookform/resolvers": "^3.9.1",
|
|
31
|
-
"@radix-ui/react-accordion": "1.2.2",
|
|
32
|
-
"@radix-ui/react-alert-dialog": "1.1.4",
|
|
33
|
-
"@radix-ui/react-aspect-ratio": "1.1.1",
|
|
34
|
-
"@radix-ui/react-avatar": "1.1.2",
|
|
35
36
|
"@radix-ui/react-checkbox": "1.1.3",
|
|
36
|
-
"@radix-ui/react-collapsible": "1.1.2",
|
|
37
|
-
"@radix-ui/react-context-menu": "2.2.4",
|
|
38
|
-
"@radix-ui/react-dialog": "1.1.4",
|
|
39
|
-
"@radix-ui/react-dropdown-menu": "2.1.4",
|
|
40
|
-
"@radix-ui/react-hover-card": "1.1.4",
|
|
41
|
-
"@radix-ui/react-label": "2.1.1",
|
|
42
|
-
"@radix-ui/react-menubar": "1.1.4",
|
|
43
|
-
"@radix-ui/react-navigation-menu": "1.2.3",
|
|
44
|
-
"@radix-ui/react-popover": "1.1.4",
|
|
45
|
-
"@radix-ui/react-progress": "1.1.1",
|
|
46
|
-
"@radix-ui/react-radio-group": "1.2.2",
|
|
47
|
-
"@radix-ui/react-scroll-area": "1.2.2",
|
|
48
|
-
"@radix-ui/react-select": "2.1.4",
|
|
49
|
-
"@radix-ui/react-separator": "1.1.1",
|
|
50
37
|
"@radix-ui/react-slider": "1.2.2",
|
|
51
38
|
"@radix-ui/react-slot": "1.1.1",
|
|
52
39
|
"@radix-ui/react-switch": "1.1.2",
|
|
53
|
-
"@radix-ui/react-tabs": "1.1.2",
|
|
54
|
-
"@radix-ui/react-toast": "1.2.4",
|
|
55
|
-
"@radix-ui/react-toggle": "1.1.1",
|
|
56
|
-
"@radix-ui/react-toggle-group": "1.1.1",
|
|
57
40
|
"@radix-ui/react-tooltip": "1.1.6",
|
|
58
41
|
"@vercel/analytics": "^1.6.1",
|
|
59
|
-
"autoprefixer": "^10.4.20",
|
|
60
42
|
"class-variance-authority": "^0.7.1",
|
|
61
43
|
"clsx": "^2.1.1",
|
|
62
|
-
"cmdk": "1.1.1",
|
|
63
|
-
"date-fns": "4.1.0",
|
|
64
|
-
"embla-carousel-react": "8.5.1",
|
|
65
|
-
"input-otp": "1.4.1",
|
|
66
44
|
"lucide-react": "^0.544.0",
|
|
67
45
|
"next": "16.1.6",
|
|
68
46
|
"next-themes": "^0.4.6",
|
|
69
47
|
"react": "19.2.3",
|
|
70
|
-
"react-day-picker": "8.10.1",
|
|
71
48
|
"react-dom": "19.2.3",
|
|
72
|
-
"react-hook-form": "^7.54.1",
|
|
73
|
-
"react-resizable-panels": "^2.1.7",
|
|
74
|
-
"recharts": "2.15.0",
|
|
75
|
-
"sonner": "^1.7.1",
|
|
76
49
|
"tailwind-merge": "^2.5.5",
|
|
77
|
-
"tailwindcss-animate": "^1.0.7"
|
|
78
|
-
"vaul": "^1.1.2",
|
|
79
|
-
"zod": "^3.24.1"
|
|
50
|
+
"tailwindcss-animate": "^1.0.7"
|
|
80
51
|
},
|
|
81
52
|
"devDependencies": {
|
|
82
53
|
"@tailwindcss/postcss": "^4.1.13",
|
package/app/globals.css
DELETED
|
@@ -1,110 +0,0 @@
|
|
|
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
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
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
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
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
|
-
}
|