Media and controls

Introduction

Every detail page starts with one 3D entry. Add image, native video or YouTube entries with unique IDs:

ts
media: [
  { id: 'model', kind: 'minimap', label: '3D view' },
  { id: 'front', kind: 'image', label: 'Front view', src: '/front.webp',
    thumbnail: '/front-small.webp', fit: 'contain', alt: 'Front of the enclosure' },
  { id: 'assembly', kind: 'video', label: 'Assembly', src: '/assembly.webm', poster: '/assembly.webp',
    captions: [{ src: '/assembly-en.vtt', language: 'en', label: 'English', default: true }] },
  { id: 'demo', kind: 'youtube', label: 'Demonstration', videoId: 'M7lc1UVf-VE' }
]

An omitted 3D entry is inserted; duplicate minimap entries are ignored for presentation. Images need meaningful alt text. Use native WebVTT tracks for relevant audio. Replace the old source-less drawing entry with a real image; missing source data produces an unavailable message.

Provide an optional thumbnail for each image to keep the media selector light. The rail loads these previews lazily; selecting an image uses its full src. A small poster serves the same purpose for videos. Without a thumbnail, the rail uses the image source directly.

Set fit: 'contain' (the default) for cut-out product images, or fit: 'cover' for photographs that should fill their frame. The same setting applies to the thumbnail and the stage. Cover preserves proportions but crops the edges. Only the selected media item is outlined. A cube identifies the 3D view; a play symbol identifies videos. Overflow arrows scroll the rail without moving the page.

The live minimap retains its spatial hover response. Image and video panels stay still: contained product images have an 8px inset and expand to the frame on hover, keeping the entire product visible. Cover photographs fill the panel and zoom by the same 8px per side. Video previews gently zoom and enlarge their play symbol. These movements respect reduced-motion preferences.

A shared fit, separate media lifetimes

Images and videos preserve proportions in the model's same fit rectangle. Selecting them hides the main scene and releases its interaction/sensor participation. Loaded model resources and camera state are retained, so returning to 3D does not require a new download.

Native video uses ordinary controls, inline playback and metadata preload. It does not autoplay. Deselection pauses the video and releases its source. YouTube uses an explicitly selected privacy-enhanced iframe without autoplay; leaving removes that iframe. Host code cannot control arbitrary cross-origin pointer/wheel events inside the player.

Navigation away and back resets media selection to 3D. Media IDs are not a substitute for model asset identity or route history. The rail reveals the selected item by adjusting its own local scroll position, without scrolling the document.

Gesture ownership

White UI controls retain their own input. A gesture keeps its initial owner until release.

StateFree-area gesture
Flow detail, 3DModel contact rotates; outside scrolls
Split detail, 3DLeft of pane controls 3D; pane scrolls
Split zoom, hidden paneEntire free viewport controls 3D
Image/videoFree areas scroll the document
Native or embedded player UIPlayer owns its controls

Ownership is selected at gesture start and does not flip when the finger crosses a region boundary. Menu, rail, pane and real player controls always take priority over model interaction.

Device orientation

SpatialCardGridSection and SpatialCarouselSection enable device orientation automatically; they have no deviceOrientation prop. Set deviceOrientation on BrandStageShell to enable detail geometry and minimaps as well. Access starts automatically where allowed. If explicit permission is needed, the framework requests it during a trusted internal navigation gesture without delaying navigation. Denial, missing sensors and reduced motion leave ordinary interaction available.

Device rotation is calibrated, smoothed and bounded. Orientation changes reset the reference. Scrolling pauses application, and release establishes a new reference before the effect fades back in.

The carousel uses a subtle shared view change with stronger rotation of the active item; mouse proximity still rotates individual items. Sensor behavior on real Android, Surface and iOS hardware requires direct checks. Desktop automation does not prove sensor axes, permission prompts or mobile browser-toolbar performance.

The executable /docs/examples/media/ fixture covers selected-media layout, player teardown, failure states and hidden-scene rendering. Its YouTube test uses a controlled iframe fixture; it does not claim validation of the remote service.