Virtual Tour Badge — Free 360° Media Overlay UI Snippet

Virtual Tour Badge · Cards · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Accessible overlay button
A real <button> with aria-expanded, not a div with an onclick.
Looping ring pulse
A pure-CSS expanding ring signals motion without a script or GIF.
Hover-expand preview
A panorama preview and description expand above the badge on hover.
Touch and keyboard support
aria-expanded toggling mirrors :hover for non-mouse input.
Outside-click dismissal
A document listener closes the preview when focus moves elsewhere.
Zero layout shift
The closed preview is absolutely positioned and never affects photo flow.
CSS-only panorama art
A conic-gradient plus a dashed ellipse fakes a 360° capture with no image.
Drop onto any photo
A self-contained overlay that layers onto an existing image or placeholder.

About this UI Snippet

Virtual Tour Badge — Flagging Richer Media Without Leaving the Grid

Screenshot of the Virtual Tour Badge snippet rendered live

Real-estate and e-commerce listings increasingly offer more than a flat photo — a 360° walkthrough, a product spin, a video tour — but that richer media is worthless if shoppers never notice it's available. The virtual tour badge is the small, unmissable overlay that sits on the photo itself and says "there's more here," with a pulsing play affordance and a hover-expand preview that gives a taste of the tour before committing to the click.

A pill badge that reads as a button, not a label

The badge combines a pulsing play-icon circle with a short label ("360° Virtual Tour") inside one rounded, semi-transparent pill anchored to the photo's bottom-left corner. It's a real <button>, so it's keyboard-focusable and announces correctly to assistive tech, unlike the <div>-with-an-onclick pattern that quietly breaks keyboard access. The same accessible-overlay-button approach shows up in virtual try-on button and other richer-media affordances layered on top of a static image.

A ring pulse that draws the eye without being obnoxious

The play icon's circular background carries a single expanding, fading ring (@keyframes vtbPulse) that loops every 2.2 seconds — long enough to register as "this is interactive and has motion" without becoming a distraction the way a fast, tight pulse would. It's pure CSS, so it costs nothing at runtime and never drifts out of sync.

Hover-expand preview, no extra request

Hovering (or tapping, via aria-expanded) the badge expands a small preview card above it showing a stylized 360° capture — a conic-gradient panorama with a dashed ellipse suggesting the camera's spherical field of view — plus a one-line description of what the tour covers. The preview is positioned and animated with opacity/transform/visibility, so it never affects the photo grid's layout while closed.

Works for touch and keyboard, not just hover

Because real hover doesn't exist on touchscreens, the badge also toggles an aria-expanded state on click/tap that the CSS listens for identically to :hover — so a tap opens the preview, and a document-level click listener closes it when the user taps elsewhere. This dual-trigger pattern is what keeps a "hover for more" UI from being a desktop-only feature; pair it with the same photo used in a property listing card or an e-commerce product quick view to flag richer media on either.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You don't have to work out the dual hover/tap trigger on your own. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain why the preview card listens to both the CSS :hover pseudo-class and the badge's aria-expanded attribute rather than picking just one, and how that combination keeps the "hover for more" pattern usable on touchscreens where true hover doesn't exist. The same assistant can help optimize it — ask whether the outside-click listener on document should instead use a focusout handler for better keyboard support, or whether the ring pulse animation should pause when the preview is open to reduce visual noise. It's also useful for extending the badge: ask it to wire the click into a real Matterport or custom panorama embed inside a modal, add a small "12 rooms" count pulled from real data, or build a second badge variant for flagging video instead of a 360° tour. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a "virtual tour badge" overlay for a photo card in plain HTML, CSS, and JavaScript — no frameworks, no external images or video.

Requirements:
- A photo placeholder area (built from CSS gradients only, no real image) with an overlay badge pinned to one corner, combining a small circular play-icon button and a short text label ("360° Virtual Tour") inside one pill-shaped element.
- The badge must be a real <button> element (for keyboard focus and screen-reader semantics), not a div with a click handler, and must expose an aria-expanded attribute reflecting whether its preview is open.
- The play icon's circular background must have a continuously looping CSS-only pulse: a ring that expands outward and fades, timed slowly enough (roughly 2 seconds per cycle) to read as "interactive" without being distracting — implemented with @keyframes and no JavaScript-driven animation loop.
- A preview panel (built from CSS only, representing a stylized 360° panorama capture, not a real image) must expand above the badge on mouse hover via the :hover pseudo-class AND independently via the button's aria-expanded="true" state, so the exact same preview works for both mouse-hover users and touch/keyboard users who toggle it by tapping or activating the button.
- Add a document-level click listener that resets aria-expanded to false when a click lands outside the badge, so the preview closes when focus moves elsewhere on touch devices.
- The closed preview must not affect the surrounding layout at all (position it absolutely, animate only opacity/transform/visibility) so it never shifts other photos in a grid when opening or closing.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA photo placeholder renders with a pulsing "360° Virtual Tour" badge in the corner.
  2. 2
    Hover the badgeA preview card expands above it with a stylized panorama shot and a description.
  3. 3
    Tap it on touchTapping toggles the same preview via aria-expanded; tapping elsewhere closes it.
  4. 4
    Watch the play iconA ring pulses outward continuously to signal the affordance is interactive.
  5. 5
    Wire up the clickOn badge click, open your real tour viewer or embed instead of just previewing.
  6. 6
    Reposition itMove .vtb-badge to any photo corner; the preview anchors relative to it.

Real-world uses

Common Use Cases

Real estate listing photos
Flag which photos on a property listing card have a full walkthrough.
E-commerce product galleries
Signal a 360° spin view on a product quick view image.
Hotel and venue galleries
Highlight rooms with an immersive tour inside a hotel room picker.
Vehicle listing photos
Mark cars with an interior 360° view, alongside a rental class card.
Event venue browsing
Show which venue photos include a walkable virtual tour.
Museum and exhibit previews
Flag exhibits with an immersive online walkthrough available.

Got questions?

Frequently Asked Questions

A real <button> is focusable by Tab, activatable with Enter or Space, and announced correctly by screen readers by default — none of which a <div> with a click handler gets without extra ARIA plumbing and manual key handling. Since this badge triggers an action (opening a tour), it belongs in the accessibility tree as an actual control.

The badge listens for a click/tap and toggles its aria-expanded attribute between true and false. The CSS selector [aria-expanded="true"] + .vtb-preview shows the preview identically to the :hover rule used for mouse users, so both input types reach the same visual state. A document-level click listener closes the preview when a tap lands outside the badge.

Add your click handler logic where the aria-expanded toggle happens — instead of (or in addition to) opening the CSS preview, launch your tour viewer (an embedded Matterport/Kuula iframe, a custom panorama library, or a modal). Keep the aria-expanded toggle for the lightweight preview state even if the full tour opens in a separate modal.

Yes — the pattern (pulsing icon, label, hover-expand preview) works for any "there's more here" flag: a product video, a floor plan overlay, a drone photo, or a 3D model viewer. Swap the play-icon SVG and label text, and change the preview's mock content to match what the flagged media actually is.

Keep it a real button bound to a boolean open state (useState in React, a ref in Vue) driving aria-expanded, and conditionally render or CSS-toggle the preview from that same state. Add an outside-click handler via a document listener in a mount effect, cleaned up on unmount, mirroring the vanilla document.addEventListener pattern shown here.