SVG Interactivity

Beginner
⏱️ 12 min read
📚 Updated: Aug 2026
🎯 5 Examples
🚀 5 Try-it labs
Advanced

What You’ll Learn

SVG is not only for static drawings — shapes can respond to clicks, hover, touch, and keyboard input. This tutorial covers event listeners, CSS :hover / :focus-visible, pointer-events, accessible button patterns, five worked examples, and how interactivity compares to SVG links.

Click

addEventListener

Toggle state, open UI, or update attributes on click.

:hover

CSS feedback

Highlight shapes with fill, opacity, and transforms.

Keyboard

tabindex

Make controls focusable and activate with Enter/Space.

A11y

aria-label

Describe interactive controls for assistive tech.

Hit Testing

pointer-events

Control which layers receive mouse and touch hits.

CSS + JS

Split roles

Use CSS for motion; use JS for state and logic.

Introduction

Interactive SVG powers dashboards, maps, icon buttons, and micro-interactions. Because SVG nodes live in the DOM, you can style them with CSS and wire them with the same JavaScript events you use on buttons and links.

The best pattern is simple: CSS for hover/focus polish, JavaScript for state changes, and accessibility attributes so keyboard and screen-reader users get the same experience.

Why it matters?

Static icons miss opportunities. Interactive SVG keeps visuals crisp at any size while behaving like real UI controls — clickable, focusable, and themeable.

Key Highlights

DOM Events

Attach click, keydown, mouseover, and more.

CSS States

:hover and :focus-visible keep feedback smooth.

Accessible Controls

tabindex, role, and aria-label unlock keyboard use.

Hit Areas

pointer-events decides what can be clicked.

In short: select a shape, style its hover/focus states, listen for click and keyboard keys, and label it for accessibility.

📝 Syntax

Core pattern for an interactive SVG control:

index.html
<svg viewBox="0 0 200 120">
  <rect id="btn" x="40" y="30" width="120" height="60" rx="12"
        fill="#2563eb" tabindex="0" role="button"
        aria-label="Interactive rectangle" />
</svg>

<script>
  var el = document.getElementById("btn");
  el.addEventListener("click", function () {
    // handle interaction
  });
</script>

Building blocks

PieceRoleDescription
clickPointer / touchPrimary activation for mice and fingers.
:hoverCSS feedbackVisual polish while the pointer is over the shape.
tabindex="0"FocusAdds the element to the keyboard tab order.
role="button"SemanticsAnnounces the control as a button to assistive tech.
aria-labelNameAccessible name when there is no visible text.
pointer-eventsHit testingEnable, disable, or pass through pointer hits.

Minimal workflow

index.html
<style>
  #toggleRect { cursor: pointer; transition: fill 160ms ease; }
  #toggleRect:hover { opacity: 0.92; }
  #toggleRect:focus-visible { outline: 3px solid #0f172a; outline-offset: 2px; }
</style>

<svg width="240" height="160" viewBox="0 0 240 160">
  <rect id="toggleRect" x="55" y="35" width="130" height="90" rx="16"
        fill="#2563eb" tabindex="0" role="button"
        aria-label="Toggle rectangle color" />
</svg>

<script>
  var rect = document.getElementById("toggleRect");
  function toggle() {
    var isBlue = rect.getAttribute("fill") === "#2563eb";
    rect.setAttribute("fill", isBlue ? "#ef4444" : "#2563eb");
  }
  rect.addEventListener("click", toggle);
  rect.addEventListener("keydown", function (e) {
    if (e.key === "Enter" || e.key === " ") {
      e.preventDefault();
      toggle();
    }
  });
</script>

⚡ Quick Reference

GoalCode
Listen for clickel.addEventListener("click", handler)
Hover style#shape:hover { fill: #ef4444; }
Make focusabletabindex="0"
Activate with keyboardHandle Enter and Space on keydown
Ignore hits on text<text pointer-events="none">
Pass clicks throughpointer-events="none" on overlays

📋 Interactivity vs Links vs CSS-only vs Canvas

Pick the lightest approach that matches the job.

SVG + JS
custom UI

Toggles, tooltips, dashboards, and custom controls

SVG <a>
navigation

Native links with href — see SVG Links

CSS only
visual feedback

Hover/focus polish without changing application state

Canvas
pixel hit tests

Harder a11y — prefer SVG when shapes are UI

Context

When to Use Interactive SVG

Reach for interactive SVG when the graphic is the interface.

  1. Icon buttons

    Toggleable glyphs with hover, focus, and click handlers.

  2. Charts & maps

    Highlight regions, show tooltips, and select series.

  3. Micro-interactions

    Press states, soft lifts, and colour toggles on shapes.

  4. Not for plain navigation

    Prefer <a> links when the only goal is going to a URL.

  5. Not hover-only UX

    Touch devices lack hover — always provide a click/focus path.

Key benefit: crisp vector UI with the same event and accessibility model as HTML.

Examples Gallery

Five starter snippets for interactive SVG. Click View Output for a live demo, or Try It Yourself to edit the full page.

📚 Getting Started

Toggle on click, then add hover feedback with a tooltip.

Example 1 — Click to Toggle State

Click (or press Enter/Space) to toggle the rectangle colour. Includes keyboard support and a focus ring.

index.html
<style>
  #toggleRect{cursor:pointer;transition:fill 160ms ease,transform 160ms ease}
  #toggleRect:hover{transform:translateY(-1px)}
  #toggleRect:focus-visible{outline:none}
  #toggleRect.is-focus{stroke:#0f172a;stroke-width:3}
</style>

<svg width="240" height="160" viewBox="0 0 240 160">
  <rect id="toggleRect" x="55" y="35" width="130" height="90" rx="16" fill="#2563eb"
        tabindex="0" role="button" aria-label="Toggle rectangle color" />
</svg>

<script>
  var rect = document.getElementById("toggleRect");
  function toggle() {
    var isBlue = rect.getAttribute("fill") === "#2563eb";
    rect.setAttribute("fill", isBlue ? "#ef4444" : "#2563eb");
  }
  rect.addEventListener("click", toggle);
  rect.addEventListener("keydown", function (e) {
    if (e.key === "Enter" || e.key === " ") {
      e.preventDefault();
      toggle();
    }
  });
  rect.addEventListener("focus", function () { rect.classList.add("is-focus"); });
  rect.addEventListener("blur", function () { rect.classList.remove("is-focus"); });
</script>
Try It Yourself

How It Works

The rectangle is a focusable button. Click and keyboard handlers call the same toggle() function so pointer and keyboard users stay in sync.

Example 2 — Hover Highlight (CSS) + Tooltip (JS)

Use CSS for hover motion and JavaScript only to show contextual tip text.

index.html
<svg width="320" height="160" viewBox="0 0 320 160">
  <style>
    .chip{cursor:pointer;transition:transform 140ms ease,opacity 140ms ease}
    .chip:hover{transform:translateY(-2px);opacity:0.95}
    .chip.is-focus rect{stroke:#0f172a;stroke-width:3}
  </style>

  <g id="chipA" class="chip" tabindex="0" role="button" aria-label="Blue chip">
    <rect x="32" y="44" width="120" height="72" rx="18" fill="#3b82f6" />
    <text x="92" y="86" text-anchor="middle" font-size="14" fill="white"
          font-family="system-ui,Segoe UI,Arial" pointer-events="none">Blue</text>
  </g>

  <g id="chipB" class="chip" tabindex="0" role="button" aria-label="Green chip">
    <rect x="168" y="44" width="120" height="72" rx="18" fill="#10b981" />
    <text x="228" y="86" text-anchor="middle" font-size="14" fill="white"
          font-family="system-ui,Segoe UI,Arial" pointer-events="none">Green</text>
  </g>
</svg>

<div id="tip">Hover or focus a chip</div>

<script>
  var tip = document.getElementById("tip");
  function bindChip(id, msg) {
    var el = document.getElementById(id);
    function show() { if (tip) tip.textContent = msg; }
    el.addEventListener("mouseover", show);
    el.addEventListener("focus", function () { el.classList.add("is-focus"); show(); });
    el.addEventListener("blur", function () { el.classList.remove("is-focus"); });
  }
  bindChip("chipA", "Blue chip selected (demo)");
  bindChip("chipB", "Green chip selected (demo)");
</script>
Try It Yourself

How It Works

CSS handles the lift-on-hover animation. JS only updates the tip string — a clean split that stays fast on touch and desktop.

📈 Practical Patterns

Keyboard focus, hit testing, and grouped targets.

Example 3 — Keyboard-First Button

A circle control that announces itself as a button and reacts to Tab + Enter/Space.

index.html
<style>
  #pulseBtn { cursor: pointer; transition: fill 160ms ease, stroke-width 160ms ease; }
  #pulseBtn:focus-visible { stroke: #0f172a; stroke-width: 4; }
</style>

<svg width="200" height="160" viewBox="0 0 200 160">
  <circle id="pulseBtn" cx="100" cy="80" r="42" fill="#8b5cf6"
          tabindex="0" role="button" aria-pressed="false"
          aria-label="Pulse button" />
</svg>
<p id="status">Pressed: false</p>

<script>
  var btn = document.getElementById("pulseBtn");
  var status = document.getElementById("status");
  function activate() {
    var pressed = btn.getAttribute("aria-pressed") === "true";
    btn.setAttribute("aria-pressed", String(!pressed));
    btn.setAttribute("fill", pressed ? "#8b5cf6" : "#22c55e");
    status.textContent = "Pressed: " + (!pressed);
  }
  btn.addEventListener("click", activate);
  btn.addEventListener("keydown", function (e) {
    if (e.key === "Enter" || e.key === " ") {
      e.preventDefault();
      activate();
    }
  });
</script>
Try It Yourself

How It Works

aria-pressed tracks toggle state for assistive tech. :focus-visible (or a focus stroke) shows where keyboard focus is.

Example 4 — pointer-events Pass-Through

Disable hits on an overlay so clicks reach the shape underneath.

index.html
<svg width="240" height="160" viewBox="0 0 240 160">
  <rect id="target" x="50" y="40" width="140" height="80" rx="12"
        fill="#2563eb" style="cursor:pointer" />
  <!-- Decorative overlay that should NOT steal clicks -->
  <rect x="50" y="40" width="140" height="80" rx="12"
        fill="white" fill-opacity="0.15" pointer-events="none" />
  <text x="120" y="88" text-anchor="middle" fill="white" font-size="14"
        font-family="system-ui,Segoe UI,Arial" pointer-events="none">Click me</text>
</svg>
<p id="hits">Clicks: 0</p>

<script>
  var n = 0;
  var target = document.getElementById("target");
  var hits = document.getElementById("hits");
  target.addEventListener("click", function () {
    n += 1;
    hits.textContent = "Clicks: " + n;
  });
</script>
Try It Yourself

How It Works

Without pointer-events="none", the overlay or text could intercept clicks. Disabling hit testing on decorations keeps the real target reliable.

Example 5 — Group as One Hit Area

Wrap a shape and label in a <g> so the whole card acts as one button.

index.html
<style>
  .card { cursor: pointer; transition: transform 140ms ease; }
  .card:hover { transform: translateY(-2px); }
  .card.is-on rect { fill: #f59e0b; }
</style>

<svg width="260" height="150" viewBox="0 0 260 150">
  <g id="card" class="card" tabindex="0" role="button" aria-pressed="false"
     aria-label="Select card">
    <rect x="40" y="30" width="180" height="90" rx="16" fill="#0ea5e9" />
    <text x="130" y="84" text-anchor="middle" fill="white" font-size="16"
          font-family="system-ui,Segoe UI,Arial" pointer-events="none">Select</text>
  </g>
</svg>

<script>
  var card = document.getElementById("card");
  function toggle() {
    var on = card.getAttribute("aria-pressed") === "true";
    card.setAttribute("aria-pressed", String(!on));
    card.classList.toggle("is-on", !on);
  }
  card.addEventListener("click", toggle);
  card.addEventListener("keydown", function (e) {
    if (e.key === "Enter" || e.key === " ") {
      e.preventDefault();
      toggle();
    }
  });
</script>
Try It Yourself

How It Works

Events bubble to the group, so one listener covers the whole card. Text uses pointer-events="none" so it never steals the click.

Use Cases

Where interactive SVG shows up in real products.

1. Icon Buttons

Toggleable toolbar glyphs with hover and focus states.

Example: like / bookmark icon.

2. Charts

Highlight series, show values, and select points.

Example: bar hover tooltip.

3. Maps

Clickable regions with keyboard-friendly labels.

Example: country select map.

4. Product Diagrams

Hotspots that reveal details on click or focus.

Example: annotated device outline.

5. Micro-animations

Press feedback and soft lifts without leaving SVG.

Example: card select pulse.

6. Learning Demos

Teach DOM events using visible vector controls.

Example: classroom click labs.

Pro Tip: if the only action is navigation, use SVG links — save JS for state changes and custom behaviour.

Advantages

Why interactive SVG beats image maps and canvas for many UIs.

  1. 1. Stays Sharp

    Vector hit targets scale cleanly on any screen.

  2. 2. Familiar Events

    Use the same listeners as HTML buttons and links.

  3. 3. Accessible When Labeled

    Focus, roles, and names work with assistive tech.

  4. 4. CSS Friendly

    Hover and focus styles without rewriting geometry.

  5. 5. Precise Hit Areas

    Target exact shapes — not rectangular image hotspots.

Pro Tip: keep motion in CSS and logic in JS — your interactions stay smoother and easier to maintain.

Usage Tips

Follow these practices for reliable interactive SVG.

  1. 1. Prefer CSS for Hover

    Animate fill, opacity, and transform with CSS whenever possible.

  2. 2. Always Add a Keyboard Path

    Use tabindex="0" and handle Enter/Space for button-like controls.

  3. 3. Label Every Control

    Provide aria-label or visible text so the action has a name.

  4. 4. Disable Hits on Labels

    Set pointer-events="none" on <text> inside clickable groups.

  5. 5. Give Thin Strokes a Hit Area

    Use a transparent wider stroke or invisible fill so targets are easy to tap.

Pro Tip: never ship hover-only behaviour — always mirror the action on click/focus for touch and keyboard users.

Common Pitfalls

Avoid these mistakes when clicks or focus seem broken.

  1. 1. Overlay Steals Clicks

    A transparent layer on top can block the real target.

    → Set pointer-events="none" on decorative overlays.

  2. 2. No Paint = No Hit

    fill="none" with no stroke may not receive clicks.

    → Add a stroke, fill, or invisible hit shape.

  3. 3. Listener Attached Too Early

    Querying the SVG before it exists returns null.

    → Run scripts after the markup, or wait for DOMContentLoaded.

  4. 4. Missing Keyboard Support

    Click-only controls exclude keyboard users.

    → Add tabindex="0" and Enter/Space handlers.

  5. 5. Hover-Only Interactions

    Touch devices never get a lasting hover state.

    → Mirror hover feedback with click/focus behaviour.

Pro Tip: if clicks fail, inspect overlays and pointer-events first — those two cause most “dead SVG button” bugs.

🧠 How Interactive SVG Works

1

Identify the target

Give the shape or group an id (or class) so JavaScript can select it after the SVG is in the DOM.

Select
2

Style hover and focus

Use CSS :hover and :focus-visible for fast visual feedback without waiting on JS.

CSS
3

Attach event listeners

Listen for click and keydown. Share one handler so pointer and keyboard stay consistent.

Events
4

Make it accessible

Add tabindex, role, and aria-label (or a <title>) so the control has a name and keyboard path.

A11y
=

A real UI control

Your SVG behaves like a button: clickable, focusable, and clear to assistive technologies.

Important Notes

  • SVG elements are DOM nodes — the same events as HTML apply.
  • Use CSS for hover/focus polish; use JS for state changes.
  • Button-like shapes need tabindex, a name, and keyboard activation.
  • pointer-events controls hit testing for overlays and text.
  • Prefer SVG links when the only goal is navigation.
  • Do not rely on hover alone — touch devices need a tap path.

Quick Takeaway: CSS for feel, JS for state, and accessibility attributes so every user can activate the control.

Browser Support

SVG DOM events, CSS :hover / :focus-visible, tabindex, and pointer-events are supported in all modern browsers. Test keyboard focus rings and touch targets on real devices.

Modern browsers

SVG Interactivity

Inline SVG participates in the same event model as HTML. Use progressive enhancement and keep hit areas large enough for touch.

100% Modern browsers
Google Chrome All versions · Desktop & Mobile
Full support
Mozilla Firefox All versions · Desktop & Mobile
Full support
Apple Safari All versions · macOS & iOS
Full support
Microsoft Edge All versions · Chromium & Legacy
Full support
Internet Explorer IE 6+ · Legacy environments
Full support
Opera All modern versions
Full support
Events & CSS Universal

Bottom line: Safe for production UI. Prefer inline SVG (not image tags) when you need events, and verify focus styles with a keyboard.

Wrap Up

🎉 Conclusion

Interactive SVG turns vector art into real controls — clickable, hoverable, and keyboard-friendly — using the same CSS and JavaScript skills you already know.

Practice the five examples above, then continue to SVG Links when navigation is the primary goal.

Label controls, support Enter/Space, and never ship hover-only interactions.

💡 Best Practices

✅ Do

  • Use CSS for hover animations and transitions whenever possible
  • Add tabindex="0", role, and an accessible name for button-like shapes
  • Handle Enter and Space the same way as click
  • Set pointer-events="none" on labels and decorative overlays
  • Keep touch targets comfortably large

❌ Don’t

  • Rely on hover-only interactions—touch devices don’t have hover
  • Attach listeners before the SVG exists in the DOM
  • Leave interactive shapes without a visible focus style
  • Use JS for navigation when a plain <a> would do
  • Expect fill="none" shapes to be easy to click without a stroke

Key Takeaways

Knowledge Unlocked

Five things to remember about SVG interactivity

Turn shapes into real UI controls.

5
Core concepts
CSS 02

Hover

:hover polish

Style
03

Keyboard

tabindex + keys

A11y
04

Hits

pointer-events

Target
🔗 05

Links

Use <a> for URLs

Nav

❓ Frequently Asked Questions

Give the SVG element an id (or select it by class), then attach a click event listener in JavaScript. For accessibility, add tabindex="0", role="button", and handle Enter/Space if it behaves like a button.
Yes. SVG elements support CSS selectors like :hover and :focus-visible, so you can change fill, stroke, opacity, and transforms on hover or keyboard focus.
pointer-events controls whether an SVG element can receive mouse/touch interactions. It is useful when you want clicks to pass through overlays or when thin strokes are hard to target.
Add tabindex="0" to the interactive element, include an aria-label (or a <title>), and listen for keydown so Enter and Space activate the same behavior as click.
Check for overlays covering the element, pointer-events settings, and whether the element has a clickable paint region (for example it is not fill="none" with no stroke). Also ensure the listener is attached after the SVG is in the DOM.
Yes for visual labels, but set pointer-events="none" on <text> so clicks hit the parent shape consistently instead of competing with the text node.

Did you Know? 🔊

SVG shapes are DOM elements — you can attach the same click, keydown, and CSS :hover / :focus-visible patterns you already use on HTML. Putting interactive markup on a group (<g>) lets one listener cover a whole card — shape, icon, and label — as a single control.

Continue to SVG Links

Learn how <a> wraps shapes for native navigation without custom click handlers.

Links tutorial →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

5 people found this page helpful