GlassKit UI
Components

NowPlaying

A media now-playing card: album art, title + artist, a scrub bar, and elapsed / remaining times. A status display for playback your app tracks — audio support in the Display webview is undocumented; verify on-device.

WeightlessMarconi Union
1:12-2:20
600 × 600 · live

Platform wishlist — built and waiting on documented audio playback in the webview. The UI ships today; the day Meta exposes the API, it plugs in. See the wishlist →

Installation

npx @glasskit-ui/cli add now-playing

Install the SDK (it provides GlassViewport, useDpad and the stylesheet), then copy these files into your project:

npm install @glasskit-ui/react
// components/lib/utils.tsexport type ClassValue = string | number | null | undefined | false;/** * Join truthy class names. Dependency-free on purpose: the lens components * style via bespoke semantic classes (no conflicting Tailwind utilities to * de-dupe), so this needs no clsx/tailwind-merge and resolves from anywhere * the registry is vendored. */export function cn(...inputs: ClassValue[]): string {  return inputs.filter(Boolean).join(" ");}/** * Accessible name from a free-form `label` prop: the label itself when it's a * plain string, otherwise undefined (a ReactNode can't become an aria-label). */export function stringLabel(label: unknown): string | undefined {  return typeof label === "string" ? label : undefined;}
// components/glasskit/now-playing.tsximport type { ReactNode } from "react";import { cn } from "../lib/utils";/** * <NowPlaying> — a media "now playing" card: album art, title + artist, a * scrub bar, and elapsed / remaining times. The art is a node you pass (an * <img>, or a gradient tile for podcasts/radio). `progress` is 0–100. Pure * display; wire transport controls with <Button>s in `controls`. RTL-safe. * * Platform note (2026-06): Meta has not documented `<audio>` / Web Audio * support for Display web apps, and the glasses' own media player owns the * speakers. Treat this as a status display for playback your app tracks * (e.g. a server-side or phone session) — verify on-device before shipping * an app that plays audio in the webview. */export function NowPlaying({  art,  title,  artist,  progress = 0,  elapsed,  remaining,  controls,  className,}: {  /** Album art — an <img> or a gradient tile. */  art?: ReactNode;  title: ReactNode;  artist?: ReactNode;  /** 0–100. */  progress?: number;  elapsed?: ReactNode;  remaining?: ReactNode;  /** Optional transport row. */  controls?: ReactNode;  className?: string;}) {  const pct = Math.max(0, Math.min(progress, 100));  return (    <div className={cn("gk-nowplaying", className)}>      <div className="gk-nowplaying__top">        {art != null ? <span className="gk-nowplaying__art">{art}</span> : null}        <div className="gk-nowplaying__meta">          <span className="gk-nowplaying__title t-readout">{title}</span>          {artist != null ? (            <span className="gk-nowplaying__artist t-body">{artist}</span>          ) : null}        </div>      </div>      <progress        className="gk-nowplaying__bar gk-progress__el"        value={pct}        max={100}        aria-label="Playback position"      />      {elapsed != null || remaining != null ? (        <div className="gk-nowplaying__times t-caption">          <span>{elapsed}</span>          <span>{remaining}</span>        </div>      ) : null}      {controls != null ? (        <div className="gk-nowplaying__controls">{controls}</div>      ) : null}    </div>  );}

Usage

<NowPlaying  art={<img src={cover} alt="" />}  title="Midnight City" artist="M83"  progress={42} elapsed="1:48" remaining="-2:31"/>

Props

Prop

Type