How SVGs actually work

Vector graphics on the web

1,817 words10 min read

SVG - Scalable Vector Graphics - is the web's native vector format. Unlike JPEGs or PNGs that store grids of pixels, SVGs describe images as shapes: lines, curves, rectangles, text. This makes them resolution-independent, scalable to any size without quality loss, styleable with CSS, and programmable with JavaScript. In the modern web, SVGs are everywhere: icons, logos, illustrations, data visualizations, and interactive graphics.

SVG has a unique position in web technology. It is both an image format and a document format - you can embed it like an image or inline it like HTML. It lives in a strange middle ground between graphics and markup, combining visual primitives with the structure and semantics of XML. This dual nature gives it remarkable flexibility but also complexity that rewards deeper understanding.

The format emerged from the 'browser wars' of the late 1990s, when competing proprietary formats threatened to fragment web graphics. The W3C developed SVG as an open standard, first released in 2001. It took years for browser support to mature, but today SVG is supported everywhere and has become the preferred format for icons, logos, and scalable graphics on the web.

XML foundation

SVG is XML - a text format with tags, attributes, and a hierarchical structure. This means you can read an SVG file in a text editor, edit it by hand, generate it programmatically, or manipulate it with standard XML tools. Unlike binary image formats, SVG is human-readable and version-control friendly. You can diff changes, merge edits, and track history just like source code.

<svg width="200" height="200" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
  <!-- A simple face icon -->
  <circle cx="50" cy="50" r="45" fill="#FFE66D" stroke="#333" strokeWidth="2"/>
  <circle cx="35" cy="40" r="5" fill="#333"/>  <!-- left eye -->
  <circle cx="65" cy="40" r="5" fill="#333"/>  <!-- right eye -->
  <path d="M 30 65 Q 50 80 70 65" fill="none" stroke="#333" strokeWidth="3"/>
</svg>

The root <svg> element defines the canvas. The width and height attributes set the element's display size in the document. The viewBox attribute defines the internal coordinate system - here, coordinates range from 0 to 100 in both dimensions regardless of display size. This separation of internal coordinates from display size is what enables resolution-independent scaling.

The viewBox: coordinate systems

The viewBox is one of SVG's most powerful and confusing features. It defines a window into an infinite coordinate space. The four numbers - min-x, min-y, width, height - specify what portion of that space to display. The rendered SVG then scales to fit its display dimensions while preserving aspect ratio (by default).

This means an SVG with viewBox='0 0 100 100' displayed at 400x400 pixels will scale all coordinates by 4. A circle with r='10' occupies 10% of the viewBox width and will render at 40 pixels radius. Change the display size to 100x100 and the same circle renders at 10 pixels - same proportions, different physical size. This is true resolution independence.

The preserveAspectRatio attribute controls what happens when the viewBox aspect ratio doesn't match the display aspect ratio. The default 'xMidYMid meet' centers the content and scales uniformly to fit. Other values can align content to edges, stretch to fill, or crop ('slice') to cover. Understanding these options is essential for responsive SVG graphics.

Basic shapes

SVG provides elements for common shapes: <rect> for rectangles, <circle> for circles, <ellipse> for ellipses, <line> for straight lines, <polyline> for connected lines, and <polygon> for closed polygons. Each has intuitive attributes: circles have cx, cy, and r; rectangles have x, y, width, and height. Rectangles can have rounded corners via rx and ry.

These basic shapes are syntactic sugar - convenient shorthand for common cases. Under the hood, the browser converts them all to paths. A circle becomes a path with four cubic Bézier arcs (you cannot draw a perfect circle with Béziers, but four arcs get very close). Understanding this helps explain why shapes and paths share most styling attributes.

The path element: SVG's workhorse

While SVG has elements for basic shapes, the <path> element can draw anything. Its 'd' attribute contains a mini-language of drawing commands: M moves the pen without drawing, L draws a line, C draws a cubic Bézier, Q draws a quadratic Bézier, A draws an elliptical arc, Z closes the path. Uppercase commands use absolute coordinates; lowercase use relative offsets.

SVG Path Commands

Generated path:
<path d="M 50 150" />
Move To(x y)

Move pen without drawing

Interactive SVG path builder. Add commands to see how paths are constructed from move, line, and curve segments.

Path data is compact but cryptic. 'M10 10 L90 90' draws a diagonal line. 'M50 10 C50 10 90 50 50 90 C50 90 10 50 50 10 Z' draws a rough diamond. Complex icons might have paths with hundreds of commands. Design tools like Illustrator and Figma export optimized path data; reading it directly requires practice but aids debugging.

<!-- Path command reference -->
<path d="
  M 10 10        /* Move to (10,10) - starting point */
  L 90 10        /* Line to (90,10) */
  L 90 90        /* Line to (90,90) */
  Q 50 110 10 90 /* Quadratic curve with control point (50,110) */
  C 0 50 0 50 10 10  /* Cubic curve back to start with two control points */
  Z              /* Close path */
"/>

Styling: fill and stroke

SVG shapes are styled with two primary properties: fill (the interior color) and stroke (the outline). Both accept color values - keywords, hex, rgb(), hsl() - or 'none' for transparent. Strokes have additional properties: stroke-width for thickness, stroke-linecap for line endings (butt, round, square), stroke-linejoin for corners (miter, round, bevel), stroke-dasharray for dashed lines.

<!-- Various stroke and fill examples -->
<circle cx="50" cy="50" r="40" 
        fill="none" 
        stroke="#3b82f6" 
        strokeWidth="4"
        strokeDasharray="8 4"/>  <!-- 8px dash, 4px gap -->

<rect x="10" y="10" width="80" height="80"
      fill="rgba(59, 130, 246, 0.3)"
      stroke="#3b82f6"
      strokeWidth="2"
      rx="8"/>  <!-- rounded corners -->

Transforms: moving and reshaping

The transform attribute applies geometric transformations to elements: translate(x, y) moves, rotate(angle) rotates around the origin, scale(x, y) resizes, skewX(angle) and skewY(angle) shear. Transforms can be chained, applying in sequence from left to right. A transform applies to an element and all its children, enabling hierarchical transformations.

Transform origin defaults to (0, 0) - the viewBox origin, not the element's center. This surprises many developers: rotating a rectangle at the bottom-right of the canvas will orbit it around the top-left. Use transform-origin (in CSS) or translate before rotating to achieve rotation around other points. SVG 2 adds transform-box to control this more intuitively.

Styling with CSS

SVG elements can be styled with CSS, either inline, in a <style> element within the SVG, or from external stylesheets when SVG is inlined in HTML. Properties like fill, stroke, stroke-width, and opacity map directly to presentation attributes. CSS takes precedence over attributes, enabling easy theming and dynamic styling.

/* Styling SVG elements with CSS */
.icon {
  fill: currentColor;  /* Inherit text color */
  stroke: none;
  transition: fill 0.2s ease, transform 0.2s ease;
}

.icon:hover {
  fill: #3b82f6;
  transform: scale(1.1);
}

/* Complex selections work too */
svg .highlight { fill: yellow; }
svg path:first-child { stroke-width: 3; }

The currentColor keyword is particularly powerful for icons. Setting fill='currentColor' makes the SVG inherit its container's text color, enabling icons that automatically match surrounding text and respond to dark/light mode without additional code. This is why many icon libraries recommend currentColor as the default fill.

Grouping and reuse

The <g> element groups child elements, enabling collective transforms, styling, and event handling. The <defs> element defines reusable components that are not rendered directly. The <use> element instantiates defined elements, optionally with different positions and styles. This enables efficient icon systems with single definitions and multiple instances.

The <symbol> element combines the best of <defs> and <g>: it defines a reusable group with its own viewBox, so it can be rendered at any size. Icon sprites - single SVGs containing many icon symbols - use this pattern. Each icon is a <symbol> with an id, instantiated via <use href='#icon-id'>.

Animation: SMIL and CSS

SVG supports animation through multiple mechanisms. SMIL (Synchronized Multimedia Integration Language) provides declarative animations embedded directly in the SVG: <animate> changes attributes over time, <animateTransform> animates transforms, <animateMotion> moves elements along paths. SMIL works without JavaScript and is fully self-contained.

CSS animations and transitions work on SVG elements just like HTML elements, though with SVG-specific properties. You can animate fill, stroke, transform, opacity, and many other properties. CSS animations are often preferred for web development because they integrate with existing tooling and workflows.

<!-- SMIL animation - moves a circle back and forth -->
<circle cx="50" cy="50" r="10" fill="blue">
  <animate 
    attributeName="cx" 
    values="50;150;50" 
    dur="2s" 
    repeatCount="indefinite"/>
</circle>

<!-- CSS animation alternative -->
<style>
  @keyframes pulse { 0%, 100% { r: 10; } 50% { r: 20; } }
  circle { animation: pulse 1s infinite; }
</style>

JavaScript manipulation

When SVG is inline in HTML, JavaScript has full access to the SVG DOM. Query selectors work, event listeners attach, attributes change dynamically. This enables interactive graphics: data visualizations that respond to hover, diagrams with draggable elements, games built on SVG primitives.

// Interactive SVG manipulation
const svg = document.querySelector('svg')
const circle = document.querySelector('circle')

svg.addEventListener('mousemove', (e) => {
  const rect = svg.getBoundingClientRect()
  const x = (e.clientX - rect.left) / rect.width * 100
  const y = (e.clientY - rect.top) / rect.height * 100
  
  circle.setAttribute('cx', x.toString())
  circle.setAttribute('cy', y.toString())
})

// Creating elements programmatically
const newCircle = document.createElementNS('http://www.w3.org/2000/svg', 'circle')
newCircle.setAttribute('r', '5')
svg.appendChild(newCircle)

Gradients and patterns

SVG supports linear and radial gradients as fill or stroke values. Gradients are defined in <defs> with <linearGradient> or <radialGradient> elements containing <stop> elements that define colors at positions. Referenced via url(#gradientId), gradients can fill any shape. Patterns work similarly, defining repeating tiles that can fill areas.

Filters: effects processing

SVG filters apply visual effects: blur, drop shadow, color manipulation, compositing. The <filter> element contains filter primitives like <feGaussianBlur>, <feDropShadow>, <feColorMatrix>. Filters can chain primitives together, piping output from one to input of another, creating complex effects from simple building blocks.

Filters are powerful but expensive - they require rendering to offscreen buffers and applying per-pixel operations. Use sparingly and test performance, especially on mobile devices. CSS filters (filter: blur(), drop-shadow()) provide a simpler API for common effects, though with less flexibility than SVG filters.

Accessibility considerations

SVGs can and should be made accessible. The <title> element provides a tooltip and accessible name - essential for icons. The <desc> element offers a longer description. The role attribute tells assistive technologies how to interpret the graphic: role='img' for decorative images, role='graphics-document' for complex diagrams.

For decorative SVGs, use aria-hidden='true' to hide them from screen readers. For meaningful graphics, ensure all information conveyed visually is also available to assistive technologies through text alternatives, ARIA labels, or supplementary HTML content.

SVG brings the power of vector graphics to the web with all the web's integration capabilities: CSS styling, JavaScript interaction, DOM manipulation, accessibility support. Mastering SVG opens the door to resolution-independent graphics, interactive visualizations, and sophisticated icon systems that enhance any web project.

How Things Work - A Visual Guide to Technology