[READ-ONLY] Mirror of https://github.com/aaronateataco/SonicBridge. proxy for web radios sonicradio.vercel.app
0

Configure Feed

Select the types of activity you want to include in your feed.

Refactor UI components and styles for improved aesthetics and usability; update color scheme and typography

aaronateataco (Jul 15, 2026, 7:11 PM +0100) 00a1842d 12afe5d8

+274 -116
+4 -4
src/App.jsx
··· 98 98 <div className="app"> 99 99 <header className="topbar"> 100 100 <div className="brand"> 101 - <span className="dial-mark" aria-hidden="true" /> 101 + <div className="dial-mark">📻</div> 102 102 <div> 103 103 <h1>SonicBridge</h1> 104 - <p className="tagline">BBC Sounds, bridged to anywhere</p> 104 + <p className="tagline">BBC Sounds Radio</p> 105 105 </div> 106 106 </div> 107 107 <div className="topbar-hint"> 108 - Point Navidrome's Internet Radio at any station's stream URL below. 108 + Stream BBC Radio to any app or player. Use the stream URLs below with Navidrome, VLC, or your favourite player. 109 109 </div> 110 110 </header> 111 111 112 - <main className="grid-wrap"> 112 + <main className="grid-wrap" style={{ paddingBottom: '6rem' }}> 113 113 {status === "loading" && <p className="status-msg">Tuning in…</p>} 114 114 {status === "error" && ( 115 115 <p className="status-msg status-error">
+270 -112
src/index.css
··· 1 - @import url("https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;600;700&family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap&display=swap"); 1 + @import url("https://fonts.googleapis.com/css2?family=Cantarell:wght@400;500;600;700&display=swap"); 2 2 3 3 :root { 4 - --bg: #0d0f14; 5 - --panel: #171a21; 6 - --panel-hi: #1f232c; 7 - --line: #2a2f3a; 8 - --text: #e8eaf0; 9 - --muted: #8b92a5; 10 - --amber: #ffb648; 11 - --amber-dim: #6b5326; 12 - --live: #5eead4; 13 - font-family: "Inter", system-ui, sans-serif; 4 + /* Adwaita Light Mode */ 5 + --bg: #ffffff; 6 + --surface: #f6f5f4; 7 + --surface-dim: #ebebeb; 8 + --border: #d0cfce; 9 + --text: #2e2c2c; 10 + --text-secondary: #5e5c5c; 11 + --accent: #0061ff; 12 + --accent-bg: #e8f2ff; 13 + --destructive: #e01b24; 14 + --success: #26a269; 15 + --warning: #f57d00; 16 + 17 + /* Dark mode support */ 18 + color-scheme: light dark; 19 + font-family: "Cantarell", "Liberation Sans", system-ui, sans-serif; 14 20 } 15 21 16 - * { box-sizing: border-box; } 22 + @media (prefers-color-scheme: dark) { 23 + :root { 24 + /* Adwaita Dark Mode */ 25 + --bg: #1e1e1e; 26 + --surface: #303030; 27 + --surface-dim: #242424; 28 + --border: #454545; 29 + --text: #ffffff; 30 + --text-secondary: #c6c6c6; 31 + --accent: #62a0ea; 32 + --accent-bg: #1a3a52; 33 + --destructive: #ff7b7b; 34 + --success: #8ff0a4; 35 + --warning: #ffbe6f; 36 + } 37 + } 38 + 39 + * { 40 + box-sizing: border-box; 41 + -webkit-font-smoothing: antialiased; 42 + -moz-osx-font-smoothing: grayscale; 43 + } 17 44 18 45 html, body, #root { height: 100%; } 19 46 ··· 21 48 margin: 0; 22 49 background: var(--bg); 23 50 color: var(--text); 24 - background-image: 25 - radial-gradient(circle at 15% 0%, rgba(255, 182, 72, 0.06), transparent 40%), 26 - radial-gradient(circle at 85% 10%, rgba(94, 234, 212, 0.05), transparent 45%); 51 + transition: background-color 0.2s, color 0.2s; 27 52 } 28 53 29 54 .app { 30 55 display: flex; 31 56 flex-direction: column; 32 57 min-height: 100vh; 58 + background: var(--bg); 33 59 } 34 60 35 61 .topbar { 36 62 display: flex; 37 - align-items: flex-end; 63 + align-items: center; 38 64 justify-content: space-between; 39 - gap: 1.5rem; 65 + gap: 2rem; 40 66 flex-wrap: wrap; 41 - padding: 2.25rem 2rem 1.25rem; 42 - border-bottom: 1px solid var(--line); 67 + padding: 1.5rem 2rem; 68 + border-bottom: 1px solid var(--border); 43 69 } 44 70 45 - .brand { display: flex; align-items: center; gap: 0.9rem; } 71 + .brand { 72 + display: flex; 73 + align-items: center; 74 + gap: 1rem; 75 + } 46 76 47 77 .dial-mark { 48 - width: 34px; 49 - height: 34px; 50 - border-radius: 50%; 78 + width: 40px; 79 + height: 40px; 80 + border-radius: 12px; 51 81 flex-shrink: 0; 52 - background: conic-gradient(from -40deg, var(--amber), var(--amber-dim) 60%, var(--amber) 100%); 53 - box-shadow: 0 0 0 3px var(--panel), 0 0 0 4px var(--line), 0 0 18px rgba(255, 182, 72, 0.35); 54 - position: relative; 55 - } 56 - .dial-mark::after { 57 - content: ""; 58 - position: absolute; 59 - top: 4px; left: 50%; 60 - width: 2px; height: 10px; 61 - background: var(--bg); 62 - transform: translateX(-50%); 82 + background: var(--accent); 83 + color: white; 84 + display: flex; 85 + align-items: center; 86 + justify-content: center; 87 + font-size: 1.5rem; 88 + font-weight: 600; 63 89 } 64 90 65 91 .brand h1 { 66 - font-family: "Space Grotesk", sans-serif; 67 - font-size: 1.4rem; 92 + font-family: "Cantarell", system-ui, sans-serif; 93 + font-size: 1.3rem; 94 + font-weight: 600; 68 95 margin: 0; 69 - letter-spacing: -0.01em; 96 + color: var(--text); 70 97 } 71 98 72 - .tagline { margin: 0.1rem 0 0; color: var(--muted); font-size: 0.85rem; } 99 + .tagline { 100 + margin: 0.2rem 0 0; 101 + color: var(--text-secondary); 102 + font-size: 0.85rem; 103 + } 73 104 74 105 .topbar-hint { 75 - color: var(--muted); 76 - font-size: 0.8rem; 77 - max-width: 320px; 106 + color: var(--text-secondary); 107 + font-size: 0.85rem; 108 + max-width: 360px; 78 109 text-align: right; 79 - line-height: 1.4; 110 + line-height: 1.5; 111 + margin-left: auto; 80 112 } 81 - .topbar-hint code { color: var(--amber); } 113 + .topbar-hint code { 114 + color: var(--accent); 115 + background: var(--accent-bg); 116 + padding: 0.15rem 0.35rem; 117 + border-radius: 4px; 118 + font-family: "monospace"; 119 + font-size: 0.8rem; 120 + } 82 121 83 - .grid-wrap { flex: 1; padding: 1.75rem 2rem 8rem; } 122 + .grid-wrap { 123 + flex: 1; 124 + padding: 2rem; 125 + overflow-y: auto; 126 + } 84 127 85 - .status-msg { color: var(--muted); font-family: "JetBrains Mono", monospace; } 86 - .status-error { color: #ff8686; } 128 + .status-msg { 129 + color: var(--text-secondary); 130 + font-size: 0.95rem; 131 + text-align: center; 132 + padding: 2rem 1rem; 133 + } 134 + .status-error { 135 + color: var(--destructive); 136 + background: var(--accent-bg); 137 + padding: 1rem; 138 + border-radius: 8px; 139 + border-left: 4px solid var(--destructive); 140 + } 87 141 88 142 .grid { 89 143 display: grid; 90 - grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); 91 - gap: 1rem; 144 + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); 145 + gap: 1.5rem; 146 + max-width: 1400px; 92 147 } 93 148 94 149 .card { 95 - background: var(--panel); 96 - border: 1px solid var(--line); 97 - border-radius: 14px; 150 + background: var(--surface); 151 + border: 1px solid var(--border); 152 + border-radius: 12px; 98 153 overflow: hidden; 99 154 cursor: pointer; 100 - transition: border-color 0.15s ease, transform 0.15s ease; 155 + transition: all 0.2s cubic-bezier(0.3, 0, 0.5, 1); 101 156 display: flex; 102 157 flex-direction: column; 158 + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08); 103 159 } 104 - .card:hover { border-color: var(--amber-dim); transform: translateY(-2px); } 105 - .card-active { border-color: var(--amber); } 160 + .card:hover { 161 + border-color: var(--accent); 162 + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12); 163 + transform: translateY(-2px); 164 + } 165 + .card-active { 166 + border-color: var(--accent); 167 + background: var(--accent-bg); 168 + } 106 169 107 170 .card-art { 108 171 position: relative; 109 - aspect-ratio: 1.9 / 1; 110 - background: var(--panel-hi); 172 + aspect-ratio: 1; 173 + background: linear-gradient(135deg, var(--surface-dim), var(--surface)); 174 + overflow: hidden; 175 + } 176 + .card-art img { 177 + width: 100%; 178 + height: 100%; 179 + object-fit: contain; 180 + padding: 0.75rem; 181 + display: block; 182 + background: var(--surface); 111 183 } 112 - .card-art img { width: 100%; height: 100%; object-fit: cover; display: block; } 113 184 114 185 .freq { 115 186 position: absolute; 116 187 bottom: 0.5rem; 117 - left: 0.6rem; 118 - font-family: "JetBrains Mono", monospace; 188 + left: 0.5rem; 119 189 font-size: 0.7rem; 120 - color: var(--amber); 121 - background: rgba(13, 15, 20, 0.75); 122 - padding: 0.15rem 0.4rem; 123 - border-radius: 5px; 124 - letter-spacing: 0.03em; 190 + color: var(--text-secondary); 191 + background: rgba(0, 0, 0, 0.05); 192 + padding: 0.25rem 0.5rem; 193 + border-radius: 4px; 194 + letter-spacing: 0.02em; 195 + font-weight: 500; 125 196 } 126 197 127 198 .live-pill { 128 199 position: absolute; 129 200 top: 0.5rem; 130 - right: 0.55rem; 201 + right: 0.5rem; 131 202 display: flex; 132 203 align-items: center; 133 204 gap: 0.35rem; 134 205 font-size: 0.65rem; 135 206 text-transform: uppercase; 136 207 letter-spacing: 0.08em; 137 - color: var(--live); 138 - background: rgba(13, 15, 20, 0.75); 139 - padding: 0.2rem 0.5rem; 140 - border-radius: 999px; 208 + font-weight: 600; 209 + color: var(--success); 210 + background: rgba(38, 162, 105, 0.1); 211 + padding: 0.3rem 0.6rem; 212 + border-radius: 12px; 141 213 } 142 214 .live-dot { 143 - width: 6px; height: 6px; 215 + width: 6px; 216 + height: 6px; 144 217 border-radius: 50%; 145 - background: var(--live); 146 - box-shadow: 0 0 6px var(--live); 218 + background: var(--success); 219 + box-shadow: 0 0 4px var(--success); 147 220 animation: pulse 1.6s ease-in-out infinite; 148 221 } 149 222 @keyframes pulse { 150 - 0%, 100% { opacity: 1; } 151 - 50% { opacity: 0.35; } 223 + 0%, 100% { opacity: 1; scale: 1; } 224 + 50% { opacity: 0.5; scale: 0.8; } 152 225 } 153 226 154 - .card-body { padding: 0.85rem 0.9rem 0.5rem; flex: 1; } 227 + .card-body { 228 + padding: 1rem; 229 + flex: 1; 230 + display: flex; 231 + flex-direction: column; 232 + } 155 233 .card-body h2 { 156 - font-family: "Space Grotesk", sans-serif; 157 234 font-size: 1rem; 158 - margin: 0 0 0.3rem; 235 + font-weight: 600; 236 + margin: 0 0 0.4rem; 237 + color: var(--text); 159 238 } 160 239 .card-body p { 161 240 margin: 0; 162 - color: var(--muted); 163 - font-size: 0.8rem; 164 - line-height: 1.35; 241 + color: var(--text-secondary); 242 + font-size: 0.85rem; 243 + line-height: 1.4; 165 244 } 166 245 167 246 .copy-row { 168 247 display: flex; 169 248 align-items: center; 170 249 justify-content: space-between; 171 - gap: 0.5rem; 250 + gap: 0.75rem; 172 251 border: none; 173 - border-top: 1px solid var(--line); 252 + border-top: 1px solid var(--border); 174 253 background: transparent; 175 - color: var(--muted); 176 - padding: 0.5rem 0.9rem; 254 + color: var(--text-secondary); 255 + padding: 0.75rem 1rem; 177 256 cursor: pointer; 178 - font-size: 0.72rem; 257 + font-size: 0.8rem; 258 + transition: all 0.15s ease; 259 + margin-top: auto; 179 260 } 180 261 .copy-row code { 181 262 overflow: hidden; 182 263 text-overflow: ellipsis; 183 264 white-space: nowrap; 184 - font-family: "JetBrains Mono", monospace; 185 - color: var(--muted); 265 + font-family: "monospace"; 266 + color: var(--accent); 267 + background: var(--accent-bg); 268 + padding: 0.25rem 0.4rem; 269 + border-radius: 4px; 270 + font-size: 0.75rem; 186 271 } 187 - .copy-row:hover { background: var(--panel-hi); color: var(--text); } 272 + .copy-row:hover { 273 + background: var(--surface-dim); 274 + color: var(--text); 275 + } 188 276 .copy-label { 189 277 flex-shrink: 0; 190 - color: var(--amber); 191 - font-family: "JetBrains Mono", monospace; 278 + color: var(--accent); 279 + font-weight: 500; 280 + font-size: 0.75rem; 192 281 } 193 282 194 283 .nowplaying { ··· 197 286 display: flex; 198 287 align-items: center; 199 288 gap: 1rem; 200 - padding: 0.85rem 1.25rem; 201 - background: rgba(23, 26, 33, 0.92); 202 - backdrop-filter: blur(10px); 203 - border-top: 1px solid var(--line); 289 + padding: 1rem 1.5rem; 290 + background: var(--surface); 291 + backdrop-filter: blur(12px); 292 + border-top: 1px solid var(--border); 293 + box-shadow: 0 -1px 4px rgba(0, 0, 0, 0.08); 294 + transition: all 0.2s ease; 295 + } 296 + .nowplaying-active { 297 + box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.12); 298 + } 299 + .np-placeholder { 300 + color: var(--text-secondary); 301 + font-size: 0.85rem; 302 + } 303 + .np-art { 304 + width: 48px; 305 + height: 48px; 306 + border-radius: 8px; 307 + object-fit: contain; 308 + background: white; 309 + padding: 0.25rem; 310 + flex-shrink: 0; 204 311 } 205 - .np-placeholder { color: var(--muted); font-size: 0.85rem; } 206 - .np-art { width: 42px; height: 42px; border-radius: 8px; object-fit: cover; } 207 - .np-info { display: flex; flex-direction: column; margin-right: auto; min-width: 0; } 208 - .np-info strong { font-family: "Space Grotesk", sans-serif; font-size: 0.9rem; } 209 - .np-info span { color: var(--muted); font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.06em; } 312 + .np-info { 313 + display: flex; 314 + flex-direction: column; 315 + margin-right: auto; 316 + min-width: 0; 317 + } 318 + .np-info strong { 319 + font-size: 0.95rem; 320 + font-weight: 600; 321 + color: var(--text); 322 + } 323 + .np-info span { 324 + color: var(--text-secondary); 325 + font-size: 0.75rem; 326 + text-transform: uppercase; 327 + letter-spacing: 0.05em; 328 + font-weight: 500; 329 + } 210 330 211 331 .np-toggle { 212 - width: 40px; height: 40px; 213 - border-radius: 50%; 214 - border: 1px solid var(--amber-dim); 215 - background: var(--amber); 216 - color: #241a06; 217 - font-size: 0.9rem; 332 + width: 44px; 333 + height: 44px; 334 + border-radius: 8px; 335 + border: 1px solid var(--border); 336 + background: var(--accent); 337 + color: white; 338 + font-size: 1rem; 218 339 cursor: pointer; 219 340 flex-shrink: 0; 341 + display: flex; 342 + align-items: center; 343 + justify-content: center; 344 + transition: all 0.2s ease; 345 + font-weight: 500; 220 346 } 221 - .np-toggle:hover { filter: brightness(1.08); } 347 + .np-toggle:hover { 348 + box-shadow: 0 2px 8px rgba(0, 97, 255, 0.2); 349 + transform: scale(1.05); 350 + } 351 + .np-toggle:active { 352 + transform: scale(0.98); 353 + } 222 354 223 - .np-volume { width: 110px; accent-color: var(--amber); flex-shrink: 0; } 355 + .np-volume { 356 + width: 120px; 357 + height: 4px; 358 + accent-color: var(--accent); 359 + flex-shrink: 0; 360 + cursor: pointer; 361 + } 224 362 225 - @media (max-width: 640px) { 226 - .topbar { padding: 1.5rem 1.2rem 1rem; } 227 - .topbar-hint { display: none; } 228 - .grid-wrap { padding: 1.25rem 1.2rem 8rem; } 229 - .np-volume { width: 70px; } 363 + @media (max-width: 768px) { 364 + .topbar { 365 + padding: 1.25rem 1.5rem; 366 + gap: 1rem; 367 + } 368 + .topbar-hint { 369 + display: none; 370 + } 371 + .grid-wrap { 372 + padding: 1.5rem; 373 + } 374 + .grid { 375 + grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); 376 + gap: 1rem; 377 + } 378 + .np-volume { 379 + width: 80px; 380 + } 381 + .nowplaying { 382 + padding: 0.75rem 1rem; 383 + gap: 0.75rem; 384 + } 385 + .np-info strong { 386 + font-size: 0.9rem; 387 + } 230 388 }