Design system

Sargam is an intentional aesthetic of Deep Obsidian Velvet and Radiant Luminous Amber. There is no light mode and no second brand colour, which is less a principle than an admission: a catalogue of golden-era Hindi film music belongs in the atmosphere of a vintage listening room bathed in warm amber glow.

Colour

Colour is a CSS custom property in OKLCH almost everywhere, rather than a value written into a class string. OKLCH because its lightness is perceptual — the accent and the heart sit at 0.82 and 0.72, so they read as harmonious siblings rather than one shouting over the other.

  • Radiant AmberPrimary actions, play button, and luminous focus highlights
  • Restrained CrimsonFavourites only. Warm rose-crimson tuned to amber lightness
  • Deep Obsidian VelvetThe deep midnight listening room the entire app sits in
  • Obsidian RailDarker indigo-slate tone so navigation recedes elegantly
  • Elevated SlateSoft glass-like panels, elevated cards and dialogs
  • PopoverFlyouts, floating menus and contextual overlays
  • Luminous WhitePrimary headings and song titles with WCAG AAA clarity
  • Slate MutedSecond-line credits, artists, counts and metadata
  • Etched BorderTen per cent translucent white for crisp hairline edges

Type

Figtree, at one weight range, for everything. It was chosen for the company it keeps: the faces music apps favour are geometric sans, Spotify's own Circular is proprietary, and Figtree is the free one that sits nearest without imitating.

Aa Dil Se Dil Mila Le

Asha Bhosle · Navrang · 1959

Body text sits at 14px with relaxed leading. Second-line detail is muted rather than smaller, so a song row stays two readable lines instead of one readable and one squinted at.

Breakpoints

The one genuinely unusual thing here. Tailwind's breakpoints ask about width; ours ask about height too.

VariantApplies when
sm:width ≥ 40rem and height ≥ 30rem
md:width ≥ 48rem and height ≥ 30rem
lg:width ≥ 64rem and height ≥ 30rem

A phone turned sideways is 844px wide and 390px tall. Judged on width alone it looks like a tablet, so the app used to rearrange itself into a layout meant for a much taller window. Width was standing in for “what kind of device is this”, and rotating a phone changes the width without changing the answer.

Components

shadcn/ui, but on Base UI rather than Radix — the part worth knowing if you are reading the source expecting asChild and finding render props instead.

alert-dialogbadgebuttoncardinputscroll-areaselectseparatorsheetskeletonslidertoggletooltip

Thirteen primitives, and everything else is composed in the app rather than installed. Lists are virtualised with react-virtuoso, because 3,916 rows is more than a browser will draw happily. Icons are lucide, at one size per context.

Idioms

Four conventions the codebase keeps to. More useful to know than any single component, and easier to show than to describe.

Edges fade rather than stop.

Artwork, overflowing titles and the backdrop all end in a mask gradient instead of a hard line.

<span className="absolute inset-y-0 left-0 w-[4.75rem] overflow-hidden
  [mask-image:linear-gradient(to_right,#000_0%,transparent_88%)]
  [-webkit-mask-image:linear-gradient(to_right,#000_0%,transparent_88%)]
  md:hidden">
  <img src={artwork(song.video)} className="size-full object-cover" />
</span>
components/player-bar.tsx — abridged. Underscores, not spaces: that is Tailwind v4's arbitrary-value syntax, and it will not compile with real ones.

Motion is opt-out everywhere.

Every backdrop ships a still beside its video, and the pair swap on the browser's own setting rather than on a preference we invented.

<video className="… motion-reduce:hidden" poster={poster} />
<img   className="hidden … motion-reduce:block" src={poster} />
components/app-backdrop.tsx — one of these is always hidden

Colour never carries meaning alone.

A toggle that shows its state in the accent also says so in the markup, so it survives a screen reader and a colour filter.

<TooltipTrigger render={
  <button
    aria-pressed={liked}
    aria-label={liked ? "Remove from favourites"
                      : "Add to favourites"} />
}>
  <Heart className={liked ? "fill-current" : ""} />
</TooltipTrigger>
components/like-button.tsx — abridged. Note the render prop: that is the Base UI idiom, where Radix would take asChild.

Tokens, never literals.

A hex in a class string is where a palette starts drifting. Even shadows quote the token's own value.

bg-primary/15  text-primary  border-heart/40
bg-[#f59e0b]   text-[#e11d48]
left: how the app writes colour. right: what it avoids.