Skip to content
alexisbohnsPublic

About

share songs variations and iterations to people

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Melogram

Music showcase for Bohns — albums and tracks streamed from Supabase, rebuilt on Next.js (App Router) with a full redesign of the Home and Album pages.

Stack

  • Next.js 16 (App Router, TypeScript, CSS Modules)
  • Supabase — public content (albums, track_overview view, tracks lyrics) plus Google sign-in and per-user likes (@supabase/ssr)
  • lucide-react icons
  • Fonts: Gloock (display) + Space Grotesk via next/font

Design notes

  • The site header renders with mix-blend-mode: color-dodge over the dark mauve background (#11090C) — its soft-grey content composites to pink.
  • Only the home carries that full header. Every other page pins the Bohns signature top-left (<SiteLogo />, fixed, the counterpart to the account button top-right) and closes with the same brand · menu · social row as a <Footer />. Both composite with color-dodge like the header.
  • Each album has a color palette theme (light / accent / deep) applied through CSS custom properties. Palettes are hardcoded in src/lib/palettes.ts for now (no Supabase model change, step 1).
  • Track durations are not stored in the database; they resolve client-side from audio metadata (preload="metadata").
  • Waveforms are precomputed and stored, not decoded in the browser (versions.waveform_peaks, 512 values normalised 0–1). New uploads compute them client-side; existing rows were filled by scripts/backfill-version-waveforms.mjs (needs ffmpeg). Same reasoning as the stored durations — see supabase/migrations/20260919010000_waveform_peaks.sql. Peaks are read only by getTrack, never by the home or album queries, so those payloads stay small.
  • Track and album copy comes in two fields: description, a short blurb for the strips and rows (kept under 200 characters, clamped to two lines), and story, long-form markdown shown on the track and album pages. Both are bilingual — a _fr column alongside each, resolved by localized() in src/lib/i18n/config.ts, falling back to English when a French value is missing.

Pages

  • / — artist header (brand, bio, social links) then two tabbed sections: a Tracks section of standalone tracks (the cover doubles as the play/pause control) under Popular / Latests tabs, and an Albums section of album cards filtered by genre. Popularity ranks a track by likes × 10 + plays ÷ 10 (play totals come from the track_play_counts view)
  • /albums/[id] — one grid on desktop: the album's cover heads a 300px column with the rail of other albums running down it, beside the album's header (name, meta, description, tiles) over its tracklist and long-form Notes. AlbumDetailCard and its hero dissolve into that grid with display: contents, so the same markup collapses to a plain column on mobile, where the rail becomes a horizontal strip below the album's content
  • /tracks/[id] — a track's own page, laid out like the album page: a left column of album context (name, N songs · M min, the album's tracklist with the viewed track marked) beside a header built like the album header (the album's vinyl, the track name, release date · status, description, and tiles carrying the like and lyrics actions), then a song visualizer — play button, waveform and duration in the song's colour — and the track's long-form notes. Reached by clicking a track's name anywhere it appears
  • /artist/albums — artist-only: rank the albums shown in the home Albums section. Reached from the account menu's Artist group (only rendered for artist members). Moves are staged locally and saved as one full ranking via the reorder_albums RPC, which re-checks membership before writing; the order lives in albums.position (see supabase/migrations/20260716000000_album_position.sql) and albums without one sort last, newest first

Playing a track queues its whole album into a global player bar: waveform timeline (wavesurfer.js) recolored per album palette, play/pause, next/previous, repeat (all by default), scrubbing, and MediaSession metadata + handlers for lock-screen controls on iOS/Android. Playback survives page navigation (the player lives above the router).

Accounts & likes

  • Sign in with Google from the account button (top-right on every page). OAuth runs through Supabase Auth: the browser client starts the flow, Google redirects back to /auth/callback, and the route handler exchanges the code for a cookie session. middleware.ts keeps the session fresh.
  • Likes are per-user and persisted in the track_likes table. The heart on each track toggles a row via the browser client (writes are guarded by row-level security), with an optimistic count and rollback on failure. The public catalog stays statically cached (ISR) — only the like state is resolved client-side, so pages don't become per-user dynamic.
  • /profile shows the signed-in user and a sign-out control; /likes lists the tracks the user has liked, most recent first.
  • Artist members (rows in artist_members) additionally get inline album editing and the /artist/albums ordering page.

Supabase clients live in src/lib/supabase/: anon.ts (public read-only content), client.ts (browser, auth + likes) and server.ts (cookie-bound, for the callback route and the protected pages). The track_likes schema + RLS are in supabase/migrations/20260710_track_likes.sql.

The hosted project must have Google enabled as an auth provider and <site>/auth/callback in its redirect allow-list. No extra environment variables are needed beyond the two below.

Development

cp .env.example .env.local   # fill in the Supabase URL + anon key
npm install
npm run dev

Required environment variables (also needed on Vercel):

Variable Description
NEXT_PUBLIC_SUPABASE_URL Supabase project URL
NEXT_PUBLIC_SUPABASE_ANON_KEY Supabase anonymous key

License

This project is licensed under the Creative Commons Attribution-NonCommercial 4.0 International License.

You are free to share, adapt, and build upon this work for non-commercial purposes only, as long as you provide appropriate credit.

🔗 Read the full license

About

share songs variations and iterations to people

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages