You Might Also Like
Virtual Tour Badge — Free 360° Media Overlay UI Snippet
Virtual Tour Badge · Cards · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
Virtual Tour Badge — Flagging Richer Media Without Leaving the Grid

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