streaming-ui-script · product detail ← Back to live examples
Live demo · commerce

A polished product detail page, generated from one program

Two-column hero with an image gallery, a sticky-feeling buy box, tabbed product info, related products, FAQ, and a trust strip. Variants, gallery, and quantity are all wired through $variables — no host code required.

Live preview

Click a thumbnail to swap the hero image. Switch the colorway to re-bind the price card and the "Add to cart" CTA. Open the "Specifications" tab for the full data sheet.

$variant = "midnight"
$qty     = "1"
$tab     = "overview"

imgMidnight = "https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=1400&q=80"
imgSunset   = "https://images.unsplash.com/photo-1546435770-a3e426bf472b?w=1400&q=80"
imgLagoon   = "https://images.unsplash.com/photo-1583394838336-acd977736f90?w=1400&q=80"

heroSrc = $variant == "midnight" ? imgMidnight : ($variant == "sunset" ? imgSunset : imgLagoon)

heroImage = Card([Image(heroSrc, "Aurora Wireless Headphones — " + $variant, null)], "elevated")

thumbStrip = Grid([
  Tile("Midnight", "moon", null, "Stealth black",      $variant == "midnight" ? "primary" : "default", Action([@Set($variant, "midnight")])),
  Tile("Sunset",   "sun", null, "Warm copper",        $variant == "sunset"   ? "primary" : "default", Action([@Set($variant, "sunset")])),
  Tile("Lagoon",   "water", null, "Cool ocean blue",    $variant == "lagoon"   ? "primary" : "default", Action([@Set($variant, "lagoon")]))
], 3, "s")

gallery = Stack([
  heroImage,
  thumbStrip
], "column", "m")

priceTag = Stack([
  TextContent("$299", "title"),
  TextContent("$349", "small"),
  Badge("14% off", "success")
], "row", "s", "end")

ratingRow = Stack([
  Rating(4.8, 5, null, 1284, "md", false),
  Spacer(),
  Tag("Free returns", "rotate-left", "sm", "info")
], "row", "m", "center")

variantPicker = FormControl(
  "Colorway",
  ToggleGroup("variant", [
    {value: "midnight", label: "Midnight", icon: "moon"},
    {value: "sunset",   label: "Sunset",   icon: "sun"},
    {value: "lagoon",   label: "Lagoon",   icon: "water"}
  ], $variant),
  null
)

qtyPicker = FormControl(
  "Quantity",
  Select("qty", [
    SelectItem("1", "1"),
    SelectItem("2", "2"),
    SelectItem("3", "3"),
    SelectItem("5", "5")
  ], null, null, $qty),
  null
)

stockBadge = Stack([
  Badge("In stock — ships today", "success"),
  Spacer(),
  TextContent("72% of warehouse", "small", "muted")
], "row", "m", "center")

ctaButtons = Stack([
  Button("Add " + $qty + " to cart",
         Action([@ToAssistant("Add " + $qty + " Aurora (" + $variant + ") to my cart")]),
         "primary",
         "button",
         "large"),
  Button("Buy now",
         Action([@ToAssistant("Buy " + $qty + " Aurora (" + $variant + ") now")]),
         "secondary",
         "button",
         "large")
], "column", "s")

perks = Stack([
  Stack([Tag("truck", null, "sm", "info"),    TextContent("Free 2-day shipping", "body")], "row", "s", "center"),
  Stack([Tag("rotate-left", null, "sm", "info"),    TextContent("30-day returns",      "body")], "row", "s", "center"),
  Stack([Tag("shield", null, "sm", "success"), TextContent("2-year warranty",     "body")], "row", "s", "center"),
  Stack([Tag("headphones", null, "sm", "primary"), TextContent("Free Aurora app",     "body")], "row", "s", "center")
], "column", "s")

buyBox = Card([
  TextContent("AURORA AUDIO", "small-heavy", "primary"),
  Header("Aurora Wireless Headphones"),
  ratingRow,
  priceTag,
  Separator("horizontal", true),
  variantPicker,
  qtyPicker,
  stockBadge,
  ctaButtons,
  Separator("horizontal", true),
  perks
], "elevated")

hero = SplitView([gallery], [buyBox], "560px")

trustStrip = Stats([
  {label: "Average rating", value: "4.8 / 5", hint: "1,284 reviews",        tone: "warning"},
  {label: "Battery life",   value: "40 hrs",  hint: "Quick-charge 10 min",  tone: "success"},
  {label: "Warranty",       value: "2 yrs",   hint: "Free repair coverage", tone: "info"},
  {label: "Shipping",       value: "Free",    hint: "Carbon-neutral",       tone: "primary"}
], "start")

overviewTab = Stack([
  Header("Engineered for the long sessions"),
  TextContent("Aurora Wireless Headphones pair a 40 mm beryllium driver with adaptive ANC and 40-hour battery life. The result: a quiet, low-fatigue listening experience whether you're mixing, on a call, or on a 14-hour flight.", "body"),
  Header("Why people choose Aurora"),
  Stack([
    Stack([Tag("sliders", null, "md", "primary"), TextContent("Studio EQ — 12 presets and a 31-band custom curve", "body")], "row", "s", "center"),
    Stack([Tag("volume-xmark", null, "md", "info"),    TextContent("Adaptive ANC + transparency that follows your environment", "body")], "row", "s", "center"),
    Stack([Tag("microphone", null, "md", "success"), TextContent("Beam-forming mics that work in cafés, offices, and wind", "body")], "row", "s", "center"),
    Stack([Tag("battery-full", null, "md", "warning"), TextContent("40 h battery · 6 h playback from a 10-min top-up", "body")], "row", "s", "center")
  ], "column", "s")
], "column", "m")

specsTab = Stack([
  Header("Specifications"),
  DescriptionList([
    DescriptionItem("Driver",        "40 mm beryllium",                 "volume-high"),
    DescriptionItem("Frequency",     "10 Hz – 40 kHz",                  "chart-line"),
    DescriptionItem("Bluetooth",     "5.3 with multipoint (2 devices)", "signal"),
    DescriptionItem("Codecs",        "AAC, aptX HD, LDAC",              "sliders"),
    DescriptionItem("ANC",           "Adaptive + transparency",         "ear-listen"),
    DescriptionItem("Battery",       "40 hrs ANC on · 60 hrs off",      "battery-full"),
    DescriptionItem("Charging",      "USB-C · 10 min = 6 hrs",          "plug"),
    DescriptionItem("Weight",        "248 g",                           "scale-balanced"),
    DescriptionItem("In the box",    "Headphones · case · USB-C · 3.5mm", "box"),
    DescriptionItem("Warranty",      "2 years · free repairs",          "shield")
  ], 2),
  Note("All specs measured at 1 mW into 32 Ω at 1 kHz, average of three sample units.", "tip")
], "column", "m")

reviewsTab = Stack([
  Header("Reviews"),
  Stack([
    Rating(4.8, 5, "Average rating", 1284, "lg", false),
    Spacer(),
    Stack([
      Badge("96% recommend", "success"),
      Button("Write a review",
             Action([@ToAssistant("Open the write-a-review form")]),
             "secondary",
             "button",
             "small")
    ], "row", "s", "center")
  ], "row", "m", "center"),
  Separator("horizontal", true),
  ChatBubble("Naomi Rivers",
             "Sound stage is huge — I can hear the room around the piano. Worth every penny.",
             "2h ago", "https://i.pravatar.cc/64?img=47", "agent", "delivered"),
  ChatBubble("Aurora team",
             "Glad you're loving them! Pro tip: enable 'Studio EQ' in the app for orchestral recordings.",
             "1h ago", "https://i.pravatar.cc/64?img=5", "agent", "delivered"),
  ChatBubble("Marc Lee",
             "Battery has lasted me three flights without charging. Bought a second pair.",
             "Yesterday", "https://i.pravatar.cc/64?img=11", "agent", "delivered"),
  ChatBubble("Ada Lovelace",
             "Calls are crystal clear even on a noisy train. The transparency mode is the best I've used.",
             "3d ago", "https://i.pravatar.cc/64?img=20", "agent", "delivered")
], "column", "m")

faqTab = Stack([
  Header("Frequently asked"),
  Accordion([
    AccordionItem("Can I pair it with two devices at once?",
      [TextContent("Yes — Bluetooth 5.3 multipoint lets you stay connected to your laptop and phone simultaneously, and Aurora swaps audio sources automatically.")]),
    AccordionItem("Is it good for calls?",
      [TextContent("Three beam-forming mics with adaptive noise reduction handle open offices, busy cafés, and windy commutes.")]),
    AccordionItem("Does it fold?",
      [TextContent("The cups fold flat so the headphones slide into the included rigid case (15 cm wide).")]),
    AccordionItem("What's the return policy?",
      [TextContent("Free 30-day returns, no restocking fee. We pay shipping both ways and the case + cables can come back in the box.")]),
    AccordionItem("Is there a wired mode?",
      [TextContent("Yes — 3.5 mm jack with a 16-bit DAC bypass for lossless playback when you don't want to use Bluetooth.")])
  ])
], "column", "m")

productTabs = Tabs([
  TabItem("overview", "Overview",       [overviewTab]),
  TabItem("specs",    "Specifications", [specsTab]),
  TabItem("reviews",  "Reviews (1,284)",[reviewsTab]),
  TabItem("faq",      "FAQ",            [faqTab])
], "overview")

related = Grid([
  MediaCard("Aurora ANC Earbuds",
            "https://images.unsplash.com/photo-1606220588913-b3aacb4d2f46?w=900&q=80",
            "Same DAC, in-ear comfort.",
            ["wireless", "in-ear"],
            "$149 · 4.7 stars",
            [Button("View", Action([@ToAssistant("Show me the Aurora earbuds")]), "secondary")]),
  MediaCard("Aurora Travel Case",
            "https://images.unsplash.com/photo-1585386959984-a4155224a1ad?w=900&q=80",
            "Hard shell, magnetic clasp.",
            ["accessory"],
            "$39 · 4.6 stars",
            [Button("View", Action([@ToAssistant("Show me the Aurora travel case")]), "secondary")]),
  MediaCard("Aurora Cable Kit",
            "https://images.unsplash.com/photo-1593305841991-05c297ba4575?w=900&q=80",
            "USB-C, 3.5 mm, airplane adapter.",
            ["accessory"],
            "$19 · 4.8 stars",
            [Button("View", Action([@ToAssistant("Show me the Aurora cable kit")]), "secondary")])
], 3, "m")

closing = Notification(
  "Free Aurora app",
  "Custom EQ, find-my-headphones, and over-the-air firmware updates.",
  "Available on iOS & Android",
  "mobile-screen",
  null,
  "info",
  false,
  [Button("Get the app",
          Action([@OpenUrl("https://example.com/aurora-app")]),
          "ghost")]
)

breadcrumbs = Breadcrumb([
  BreadcrumbItem("Home",         null),
  BreadcrumbItem("Audio",        null),
  BreadcrumbItem("Headphones",   null),
  BreadcrumbItem("Aurora Wireless", null)
])

root = Stack([
  breadcrumbs,
  hero,
  trustStrip,
  Section([productTabs],   "Product details"),
  Section([related],       "Customers also buy"),
  closing
], "column", "xl")

What's powerful here

The hero uses SplitView to put the image gallery and the buy box side-by-side; the gallery's Tile thumbnails and the buy-box ToggleGroup both drive the same $variant, which a ternary uses to pick the hero image. Tabs isolate the long-form product info, and the "Add to cart" button reads back $qty and $variant so its label and outgoing message stay in sync.