HTML SVG Basics: A Practical, Modern Guide

When I build UI that needs to look crisp on a phone, a 4K monitor, and everything in between, bitmap images quickly become a liability. The moment a button icon gets scaled up or a diagram needs a tiny tweak, I either accept blurry pixels or re-export assets. SVG fixes that. It’s just XML, so I can read it, edit it, style it, animate it, and even generate it with scripts. You should think of SVG as “HTML for shapes”: semantic, inspectable, and editable.

In this guide I’ll walk you through the core SVG elements you need to draw common shapes, style them with CSS, and make them interactive. I’ll also show you how SVG compares to canvas, where SVG shines, and where it’s the wrong choice. The goal is to get you writing clean, maintainable SVG that fits into modern workflows without surprises.

What SVG actually is and why it behaves differently

SVG stands for Scalable Vector Graphics. It defines vector-based graphics in XML format. Unlike a bitmap image that stores pixels, SVG stores shape instructions: “draw a line from here to there,” “fill this circle with gray,” and so on. That means the browser can scale it to any size without losing quality.

I recommend treating SVG as part of your DOM. Each shape is an element. Each attribute is inspectable. Every path, circle, and polygon can be animated and targeted by CSS or JavaScript. That’s the mental model that makes SVG click.

Here’s a minimal SVG in a page so you can see the structure:





SVG Starter

svg { border: 1px dashed #ccc; }

The key idea: SVG shapes are not pixels. They’re instructions. The browser renders them at any resolution.

SVG vs Canvas: choose intentionally

You should choose SVG for graphics that benefit from DOM-level control: icons, diagrams, charts, or interactive UI. Use canvas for pixel-heavy work like games or large particle effects where the scene updates every frame. Here’s a direct comparison so the choice stays clear:

SVG

Canvas

A markup language for 2D graphics in XML

A scripting API that draws 2D graphics on the fly with JavaScript

The browser re-renders shapes when attributes change

Once drawn, pixels are not tracked by the browser

Resolution-independent

Resolution-dependent

Supports event handlers on elements

No built-in event handlers on drawn pixelsA simple analogy I use: SVG is like HTML text you can select and style, while canvas is like painting on a wall. You can repaint the wall, but you can’t grab a single stroke and change its color without repainting everything.

Core SVG attributes you’ll use every day

Before drawing shapes, get comfortable with the attributes that control SVG layout and scaling. They’re the difference between a graphic that “just works” and one that breaks on every screen size.

  • width and height: the displayed size in CSS pixels.
  • viewBox: the internal coordinate system, written as minX minY width height.
  • preserveAspectRatio: controls how the graphic scales inside its container.

I usually set viewBox and let CSS handle the displayed size. That keeps graphics responsive without additional math.

<svg

viewBox="0 0 300 150"

width="100%"

height="auto"

role="img"

aria-label="Responsive SVG">

If the viewBox feels strange, think of it as a camera lens: it defines the drawing’s coordinate space, while width and height define how big the photo is on screen.

Understanding viewBox with a mental grid

I treat the viewBox as a reusable grid. If the viewBox is 0 0 100 100, then I know the center is always at 50,50 and the right edge is always at 100. That makes positioning predictable. When I receive an SVG from a design tool, I normalize the viewBox to round numbers and update positions to match so future edits are simpler. It’s a small upfront effort that saves a lot of time later.

preserveAspectRatio in practical terms

The default value is xMidYMid meet, which means “keep the aspect ratio, center it, and fit it inside the box.” If you want the SVG to fill a container and you’re okay with cropping, use xMidYMid slice. When I’m building backgrounds or hero illustrations, I often pick slice for a full-bleed effect. For icons and diagrams, I keep the default.

Drawing the basics: line, circle, rectangle, polygon

Most SVG graphics are built from a small set of shapes. I recommend mastering these before moving to paths.

Line

A line is the simplest: just two points. The example below draws a blue diagonal line.





SVG Line


Welcome to the SVG line demo

Circle

The circle element uses a center point and radius. This is a common building block for icons and charts.





SVG Circle







Rectangle

Rectangles are perfect for UI mockups and background shapes.





SVG Rectangle







Rounded rectangle

A rectangle with rounded corners is just a rectangle with rx and ry values.





SVG Rounded Rectangle



<rect x="80" y="20" rx="20" ry="20" width="200" height="120"

fill="orange" stroke="black" stroke-width="2" opacity="0.5" />

Polygon (star)

Polygons connect a list of points. This example draws a simple star shape.





SVG Polygon



<polygon points="100,10 40,198 190,78 10,78 160,198"

fill="gray" stroke="orange" stroke-width="5" fill-rule="evenodd" />

These shapes alone can get you a long way. For custom icons and illustrations, you’ll eventually use paths, but I recommend starting with simple shapes because they are easier to read and maintain.

Styling SVG with CSS and keeping it accessible

SVG supports both inline attributes and CSS. I prefer CSS for maintainability, especially when you want theme changes or hover effects.





SVG CSS Styling

.badge { fill: #f7c948; stroke: #1f2937; stroke-width: 2; }

.badge:hover { fill: #f59e0b; }

.label { font: 600 14px "JetBrains Mono", monospace; fill: #111; }

SVG BADGE

For accessibility, add role="img" and an aria-label if the SVG conveys meaning. If it’s purely decorative, add aria-hidden="true" so screen readers skip it.

Also note that SVG text uses its own coordinate system. This is where the viewBox matters: you’re essentially positioning text on a canvas. If the text looks off, check your viewBox values first.

CSS variables for theming

I like to expose fill and stroke colors as CSS variables so the same SVG works in multiple themes without duplication:


:root {

--icon-stroke: #111;

--icon-fill: #e2e8f0;

}

.themed-icon {

fill: var(--icon-fill);

stroke: var(--icon-stroke);

stroke-width: 2;

}

This is especially useful for design systems. You define the theme once and the icons just inherit it.

Accessibility patterns I rely on

  • If the SVG is part of a button that already has text, I set aria-hidden="true" so the icon doesn’t duplicate the label.
  • If the SVG is standalone and meaningful, I use role="img" and aria-label to describe it.
  • If the SVG is complex (like a chart), I add a nearby text description and reference it with aria-labelledby.

These small details make SVG work for everyone, not just visual users.

Simple animation and interactivity

Every SVG element can be animated with CSS or JavaScript. That’s one of SVG’s biggest strengths for UI work. Here’s a simple pulse animation on a circle to show focus or status.





SVG Animation

.pulse {

transform-origin: 100px 100px;

animation: pulse 1.2s ease-in-out infinite;

}

@keyframes pulse {

0% { transform: scale(1); opacity: 0.9; }

50% { transform: scale(1.12); opacity: 0.6; }

100% { transform: scale(1); opacity: 0.9; }

}

If you need interactions like clicking or dragging, you can attach event listeners to SVG elements just like you do with any DOM node. That means you can build an interactive diagram without a canvas render loop.

Event handling example: interactive toggle

Here’s a simple “toggle” chip that switches state on click. It uses currentColor so the UI inherits theme colors:





SVG Toggle

.chip { cursor: pointer; }

.chip .bg { fill: #e2e8f0; stroke: #0f172a; stroke-width: 2; }

.chip .label { font: 600 14px "Space Grotesk", sans-serif; fill: #0f172a; }

.chip.active .bg { fill: #4f86f7; }

.chip.active .label { fill: #fff; }

ENABLED

const chip = document.querySelector(‘.chip‘);

chip.addEventListener(‘click‘, () => chip.classList.toggle(‘active‘));

That’s a full interactive component with no canvas. The SVG is just part of the DOM, so CSS and JS are straightforward.

Practical patterns I use in real projects

SVG works best when you treat it as a component. In modern projects (2026 workflows), I often generate SVGs from design tokens, then store them as inline components. That keeps them themeable and accessible. Here are some patterns that save time.

1) Keep icons small and inline

Inline SVG makes it easy to style with CSS and keep crisp rendering at any size. You can also add currentColor so the icon inherits text color.




2) Use viewBox to make assets responsive

If a designer gives you an SVG with fixed width and height, add a viewBox and then control size in CSS. This prevents scaling artifacts.

3) Make repeated shapes with and

When you need multiple identical shapes, define them once and reuse them. This keeps the markup clean and small.









In my experience, this approach keeps SVGs readable and makes them easier to tweak or animate.

4) Componentize with data attributes

When I build SVG-driven UI, I add data- attributes for hooks, not classes, so CSS stays visual and JS stays behavioral:



Online


const dot = document.querySelector(‘[data-status="dot"]‘);

dot.addEventListener(‘mouseenter‘, () => dot.setAttribute(‘fill‘, ‘#10b981‘));

This keeps styling flexible and reduces class clutter.

Common mistakes and how I avoid them

SVG is friendly, but there are a few traps that show up often:

  • Forgetting viewBox: Without it, scaling can stretch or clip your drawing. I always add it, even for small icons.
  • Mixing CSS pixels and SVG units: SVG uses its own coordinate system. Set the viewBox and stick to that unit system for layout.
  • Relying on inline styles for large graphics: It becomes hard to update. Use CSS classes once shapes exceed a handful of elements.
  • Missing accessibility labels: If the SVG is meaningful, add role="img" and aria-label. If not, hide it.
  • Overusing filters and blurs: They can be expensive. For a simple glow, I keep it subtle or bake it into a static asset.

I recommend adding a quick lint step if your build system allows it. Even a simple script that checks for missing viewBox values can save you from broken scaling later.

Another common pitfall: invisible strokes

In SVG, strokes are centered on the shape boundary. If you draw a rectangle from 0,0 to 100,100 with a stroke-width of 4, two pixels of that stroke can be outside the viewBox and appear clipped. I avoid this by either adding padding in the viewBox or offsetting shapes by half the stroke width.

Text alignment surprises

SVG text anchors on the baseline, not the center. If you want centered text, use text-anchor="middle" and set dominant-baseline="middle":



Centered

This detail alone makes a lot of SVG text look “right.”

When to use SVG and when to avoid it

You should use SVG when you need crisp scaling, interactivity, or styling with CSS. Examples: UI icons, charts, diagrams, logos, and small illustrations.

You should avoid SVG for:

  • Large photo-like images: Use JPEG or AVIF.
  • Highly animated scenes with thousands of elements: Canvas or WebGL will be smoother.
  • Complex UI backgrounds that never change: A static bitmap can be faster to parse and render.

If your asset needs DOM-level interactivity or responsive scaling, SVG is usually the best choice. If it’s mostly pixels and updates every frame, move to canvas.

Performance notes that matter in practice

SVG is generally fast, but the DOM is not free. I watch for these patterns:

  • Element count: Hundreds of nodes are fine. Thousands can hurt, especially on mobile.
  • Filters and masks: They can cost noticeable time per frame on lower-end devices if overused.
  • Animation type: Transform and opacity are usually smooth. Animating layout attributes can be slower.

If you feel your SVG is heavy, try simplifying paths or combining shapes where it doesn’t affect readability. I also test on a mid-range phone, not just a desktop display.

A practical performance checklist

When I’m optimizing an SVG, I use this quick checklist:

1) Remove hidden layers and unused groups.

2) Convert repeated shapes into + .

3) Simplify path data if it came from a design export with excessive points.

4) Prefer CSS transforms to animating path coordinates.

5) Avoid heavy filters like blur unless the effect is essential.

It’s rare that I need all five, but even one or two can make a huge difference.

Deepening the basics: paths without fear

Most SVG guides stop at basic shapes, but real icons and illustrations rely on . The good news: you don’t need to memorize every command. You just need to recognize the pattern.

A path is defined by d, a string of commands. A small example:




  • M means “move to” (start a new point).
  • L means “line to”.
  • Z closes the shape.

That’s enough to understand most icon paths. When I need a new shape, I usually draw it in a vector editor and then inspect the path. Over time, the commands become familiar. For basic edits, you can often tweak numbers without redrawing anything.

A practical path tweak

If a path looks too narrow, you can scale it with a transform or adjust the viewBox. I usually prefer the viewBox approach so the path remains clean, but when I need a quick adjustment I’ll do this:


The transform scales the path while keeping the overall icon size consistent. It’s a quick fix that doesn’t require recalculating points.

Gradients, strokes, and fills you’ll actually use

SVG supports gradients and patterns, but I keep things simple in UI work: linear gradients for depth, subtle strokes for separation, and solid fills for clarity.

Linear gradient example










Gradients can quickly make a UI element feel polished. I keep the contrast modest so it doesn’t overpower surrounding text.

Stroke style notes

  • stroke-linecap="round" gives a friendly look for icons.
  • stroke-linejoin="round" avoids sharp corners in line icons.
  • vector-effect="non-scaling-stroke" keeps strokes consistent when scaling an SVG.

That last one is particularly useful for icons that appear at multiple sizes.

Real-world scenario: building a responsive infographic

Let’s say you need a simple infographic with a title, three dots, and labels. SVG is perfect for this because it scales cleanly and remains accessible.


Process Overview





Discover



Design



Deliver


I use to group each step and transform to position them. It keeps the layout tidy and makes later changes easy. If the spacing needs adjustment, I tweak the group positions without touching the internal elements.

Edge cases: embedding, sizing, and layout quirks

SVG behaves slightly differently depending on how you embed it. Here are the patterns I rely on.

Inline SVG (recommended)

Inline SVG gives you full CSS and JS control. It’s my default for icons and UI components.

SVG

This is easy but limits your control. CSS won’t reach inside the SVG unless it’s inline. I use this for static assets where no theming or interactivity is needed.

Background image SVG

Using an SVG as a CSS background is useful for decorative patterns. I keep these minimal and avoid complex gradients or filters because they can be harder to debug.

width="100%" and height="auto" gotcha

Some browsers don’t respect height="auto" on inline SVG. I usually set height in CSS instead:


.hero-graphic { width: 100%; height: 240px; }

...

This keeps the layout predictable across browsers.

Alternative approaches: solving the same problem differently

Sometimes there’s more than one good way to do something with SVG. Here are a few alternatives I use depending on the situation.

Icons: inline vs sprite

  • Inline SVG: best for small sets and easy styling.
  • SVG sprite (single file with symbols): best when you have dozens of icons and want to reduce markup duplication.

A sprite example:









Shapes: many elements vs a single path

If I need a clean, editable shape, I use basic elements. If I need precise control or a compact file, I use a path. The rule of thumb: if it’s a rectangle or circle, keep it literal. If it’s a complex shape, use a path.

Animation: CSS vs SMIL

  • CSS animations are straightforward and flexible.
  • SMIL animations () can be more declarative, but I avoid them for complex UIs.

In most modern projects, CSS or JS gives me enough control without relying on SMIL.

Production considerations: maintaining SVG at scale

SVG is easy for small projects, but at scale you need conventions. Here’s what I use in teams:

Naming and organization

  • Prefix class names like .icon- or .chart- to avoid collisions.
  • Use consistent viewBox sizes for icon sets (like 0 0 24 24).
  • Keep a shared file for reusable if you use them across components.

Review and linting

I add a lint step for SVG assets that checks:

  • viewBox exists
  • No inline width and height for icon files
  • No unnecessary metadata (like editor comments)

A simple check can prevent a lot of rendering bugs.

Versioning and change visibility

SVG diffs are readable, which is a huge advantage. If someone changes a path or a color, the diff tells you exactly what happened. I encourage reviewers to read SVG changes like they would read HTML or CSS.

Modern workflows in 2026

In 2026, SVG fits neatly into component-based UI and AI-assisted pipelines. I often generate baseline icons with a design tool, then run a small script to normalize attributes, remove metadata, and enforce a viewBox. For teams, I recommend keeping SVG assets in the same repo as the UI code so that review and maintenance stay simple.

AI assistants can help generate or refine SVG markup, but I always audit the output. A quick pass to remove unused attributes, align viewBox values, and name classes meaningfully makes the difference between “it works” and “it’s maintainable.”

If you’re working with frameworks, inline SVG components are still the most flexible option. You get CSS control, fast updates, and straightforward testing.

A lightweight normalization checklist

When I get an SVG from a tool or AI, I do this:

1) Ensure viewBox is present and uses sensible numbers.

2) Remove inline width and height if the asset should be responsive.

3) Delete unnecessary metadata and hidden layers.

4) Convert hard-coded colors to CSS variables or currentColor if it’s an icon.

5) Run a quick visual check at small and large sizes.

This keeps assets consistent and avoids future surprises.

A small, complete “logo” example

Here’s a simple logo-like mark that combines shapes and text. It shows how you can build brand-like visuals with a few elements.





SVG Logo

.ring { fill: none; stroke: #111; stroke-width: 6; }

.core { fill: #ff6b4a; }

.wordmark { font: 700 18px "Space Grotesk", sans-serif; fill: #111; }

ORBITAL

You can drop this into any HTML page, tweak colors in CSS, and scale it without re-exporting anything.

Practical next steps you can take today

SVG rewards small experiments. I suggest these steps:

1) Convert a simple icon you currently use into inline SVG and style it with currentColor.

2) Add viewBox to an existing SVG and test it across multiple screen sizes.

3) Build a small status indicator using only and CSS animation.

4) Audit one graphic for accessibility: add role, aria-label, or aria-hidden as needed.

When you work this way, SVG stops being a mysterious asset format and becomes part of your normal UI toolkit. That’s the point where design tweaks take minutes instead of hours, and your visuals stay sharp on every device.

If you want to go deeper after this, focus on paths () and gradients ( and ). Those two areas open up most of the expressive power of SVG, and they give you the tools to create icons, diagrams, and illustrations that stay crisp at any size.

Scroll to Top