Image Hotspots
An annotated image with glowing pin markers, each opening a small popover of detail content. Built for product-feature callouts, annotated screenshots, and simple maps.
<arc-image-hotspots> Overview
Image Hotspots lays glowing pins over a picture and gives each pin a popover. Slot the image and the <arc-hotspot> children together — every pin positions itself from its own x and y attributes, given as percentages of the image, so source order never matters and the markup stays a flat list of facts about the picture.
Each pin is a real button: keyboard-focusable, labeled for screen readers, and carrying aria-expanded popover semantics. Clicking or activating a pin opens its popover anchored above the pin, flipping to fit near viewport edges. The parent keeps one popover open at a time, and an open popover closes on Escape, on a click anywhere else, or on a second click of its pin.
Every close goes through one method, and it can be vetoed. A hotspot's close() fires the cancelable arc-close before it does anything, so a listener calling preventDefault() keeps the popover open — whoever asked for the close. That includes the parent: enforcing one-open-at-a-time is itself a close() call, so a hotspot holding an unsaved note can refuse to be closed by the pin you just clicked, not only by the user pressing Escape. Call it yourself to dismiss a popover from script; the optional first argument, restoreFocus, defaults to true and returns focus to the pin that opened it — pass false when you are closing one popover in order to open another, so focus lands on the new pin instead of bouncing back.
The pins render server-side at their coordinates because positioning is pure CSS derived from attributes; only the popover interaction needs JavaScript. Every hotspot reports its activity through arc-open and arc-close events that bubble to the parent, with detail.value carrying the hotspot's label — or its index when no label is set.
Guidelines
When to use
- Give every hotspot a label — it names the pin for screen readers, heads the popover, and identifies the hotspot in events
- Keep popover content to a sentence or two; link out for anything longer
- Place pins on the feature they describe, not beside it — coordinates are the whole message
- Use a handful of pins per image; three to six is the comfortable range
- Constrain the component to a readable width so pin targets stay comfortably apart
- Cancel `arc-close` while a popover holds unsaved input — the veto applies to the parent’s auto-close too, not just to Escape
- Pass `close(false)` when your own code closes one popover in order to open another, so focus follows the user forward
When not to use
- Do not crowd pins so close together that their popovers cover each other's targets
- Do not put critical information only in a popover — undiscovered pins go unread
- Do not use Image Hotspots for step-by-step onboarding — pins are a flat set with no order, and nothing here sequences them (`arc-guided-tour` was cut in v4; `arc-tour` is the planned rebuild)
- Do not rely on pixel positions in your head; x and y are percentages of the image box
Features
- Percentage-based pin coordinates — responsive by construction, no measuring
- Pulsing accent pins with a glow that rises on hover and focus
- One popover open at a time, coordinated by the parent
- Escape and outside-click both dismiss the open popover
- Popovers flip and shift to stay inside the viewport
- Pins are real buttons: focusable, labeled, `aria-expanded` state
- `arc-open` / `arc-close` events with `detail.value` naming the hotspot
- `arc-close` is cancelable — `preventDefault()` vetoes the close, including the parent’s one-at-a-time enforcement
- `close(restoreFocus)` dismisses a popover from script, with focus return under your control
- Pins server-render at their positions; popovers stay closed without JS
- Ambient pulse is suppressed under `prefers-reduced-motion`
Preview
Usage
Image Hotspots is in the marketing domain group, so it is
deliberately absent from the default @arclux/arc-ui barrel. Import it from
@arclux/arc-ui/marketing, or from its own subpath
@arclux/arc-ui/image-hotspots. Everything else — the element, the CSS, the
framework wrappers, the support it gets — is unchanged.
This component requires JavaScript. No pure HTML/CSS version is available — use the Web Component directly or a framework wrapper.
<arc-image-hotspots>
<img src="/screenshots/dashboard.png" alt="Analytics dashboard" />
<arc-hotspot x="18" y="30" label="Navigation">
Jump between reports, alerts, and settings from one rail.
</arc-hotspot>
<arc-hotspot x="60" y="45" label="Live charts">
Metrics stream in real time — no refresh required.
</arc-hotspot>
<arc-hotspot x="85" y="78" label="Exports">
Any panel exports to CSV, PNG, or a shared link.
</arc-hotspot>
</arc-image-hotspots> import { ImageHotspots, Hotspot } from '@arclux/arc-ui-react';
export default function FeatureTour() {
return (
<ImageHotspots onArcOpen={(e) => console.log('opened', e.detail.value)}>
<img src="/screenshots/dashboard.png" alt="Analytics dashboard" />
<Hotspot x={18} y={30} label="Navigation">
Jump between reports, alerts, and settings from one rail.
</Hotspot>
<Hotspot x={60} y={45} label="Live charts">
Metrics stream in real time — no refresh required.
</Hotspot>
<Hotspot x={85} y={78} label="Exports">
Any panel exports to CSV, PNG, or a shared link.
</Hotspot>
</ImageHotspots>
);
} <script setup>
import { ImageHotspots, Hotspot } from '@arclux/arc-ui-vue';
</script>
<template>
<ImageHotspots>
<img src="/screenshots/dashboard.png" alt="Analytics dashboard" />
<Hotspot :x="18" :y="30" label="Navigation">
Jump between reports, alerts, and settings from one rail.
</Hotspot>
<Hotspot :x="60" :y="45" label="Live charts">
Metrics stream in real time — no refresh required.
</Hotspot>
<Hotspot :x="85" :y="78" label="Exports">
Any panel exports to CSV, PNG, or a shared link.
</Hotspot>
</ImageHotspots>
</template> <script>
import { ImageHotspots, Hotspot } from '@arclux/arc-ui-svelte';
</script>
<ImageHotspots>
<img src="/screenshots/dashboard.png" alt="Analytics dashboard" />
<Hotspot x={18} y={30} label="Navigation">
Jump between reports, alerts, and settings from one rail.
</Hotspot>
<Hotspot x={60} y={45} label="Live charts">
Metrics stream in real time — no refresh required.
</Hotspot>
<Hotspot x={85} y={78} label="Exports">
Any panel exports to CSV, PNG, or a shared link.
</Hotspot>
</ImageHotspots> import { Component } from '@angular/core';
import { ImageHotspots, Hotspot } from '@arclux/arc-ui-angular';
@Component({
imports: [ImageHotspots, Hotspot],
template: `
<arc-image-hotspots>
<img src="/screenshots/dashboard.png" alt="Analytics dashboard" />
<arc-hotspot x="18" y="30" label="Navigation">
Jump between reports, alerts, and settings from one rail.
</arc-hotspot>
<arc-hotspot x="60" y="45" label="Live charts">
Metrics stream in real time — no refresh required.
</arc-hotspot>
<arc-hotspot x="85" y="78" label="Exports">
Any panel exports to CSV, PNG, or a shared link.
</arc-hotspot>
</arc-image-hotspots>
`,
})
export class FeatureTourComponent {} import { ImageHotspots, Hotspot } from '@arclux/arc-ui-solid';
export default function FeatureTour() {
return (
<ImageHotspots>
<img src="/screenshots/dashboard.png" alt="Analytics dashboard" />
<Hotspot x={18} y={30} label="Navigation">
Jump between reports, alerts, and settings from one rail.
</Hotspot>
<Hotspot x={60} y={45} label="Live charts">
Metrics stream in real time — no refresh required.
</Hotspot>
<Hotspot x={85} y={78} label="Exports">
Any panel exports to CSV, PNG, or a shared link.
</Hotspot>
</ImageHotspots>
);
} import { ImageHotspots, Hotspot } from '@arclux/arc-ui-preact';
export default function FeatureTour() {
return (
<ImageHotspots>
<img src="/screenshots/dashboard.png" alt="Analytics dashboard" />
<Hotspot x={18} y={30} label="Navigation">
Jump between reports, alerts, and settings from one rail.
</Hotspot>
<Hotspot x={60} y={45} label="Live charts">
Metrics stream in real time — no refresh required.
</Hotspot>
<Hotspot x={85} y={78} label="Exports">
Any panel exports to CSV, PNG, or a shared link.
</Hotspot>
</ImageHotspots>
);
} Hotspot
<arc-hotspot> A single glowing pin inside an Image Hotspots surface. The x and y attributes position it as percentages of the image, the label names it, and slotted children become the popover body.
-
labelstring'' - Accessible name for the pin button, repeated as the heading of the popover. Always set it — without a label the pin announces nothing useful to a screen reader.
-
xnumber50 - Horizontal position of the pin as a percentage of the image width, from 0 (left edge) to 100 (right edge). Values outside the range are clamped; a non-numeric value falls back to 50.
-
ynumber50 - Vertical position of the pin as a percentage of the image height, from 0 (top edge) to 100 (bottom edge). Values outside the range are clamped; a non-numeric value falls back to 50.
-
openbooleanfalse - Whether the pin's popover is currently visible. Reflected as an attribute. Opens on click; closes on Escape, outside click, or a second click on the pin.
Methods
-
close(restoreFocus?)restoreFocus?: boolean - Close the popover, firing the cancelable
arc-closefirst — so a listener that callspreventDefault()keeps it open, whoever asked for the close. arc-image-hotspots calls this to enforce one-open-at-a-time, which is why a consumer's veto is honoured there too and not only on a manual close.
Events
-
arc-opendetail: {value: string | number} - Fired when the popover opens. detail.value carries the label, or the pin's index within its parent when no label is set.
-
arc-closedetail: {value: string | number} - Fired before the popover closes and cancelable — preventDefault() vetoes the close. detail.value matches arc-open.
See Also
- Popover Floating content panel anchored to a trigger element, with four placement positions and automatic outside-click dismissal.
- Tooltip Contextual hint that appears on hover or focus, providing supplementary information without cluttering the UI. Supports four placement positions and a configurable show delay.
- Image Enhanced image component with shimmer loading skeleton, smooth fade-in transition, error fallback, and aspect ratio presets.
- Hotspot