Lesson 1
The SVG Coordinate System
Before you can draw, animate, or transform anything in SVG, you need a rock-solid mental model of how SVG thinks about space. This lesson covers the three foundational concepts: the viewport, the viewBox, and the user coordinate system.
The Core Mental Model
Every SVG establishes its own coordinate system - separate from the HTML document's flow. Understanding this system is the difference between "I tweaked numbers until it looked right" and "I know exactly where things will land."
Imagine a film crew shooting a scene. There are three distinct things involved:
| SVG concept | Film analogy | What it controls |
|---|---|---|
| User coordinate system | The stage (infinite, fixed) | The abstract space where all shapes are placed. It exists whether anyone is looking at it or not. |
viewBox |
The camera (position + zoom) | Selects which rectangle of the stage is captured. Move it to pan; widen/narrow it to zoom out/in. |
Viewport (width/height) |
The screen showing the footage | The physical size of the rendered output in the page. Doesn't change what's filmed - only how big the picture appears. |
The stage never moves. The shapes never move. When you change the viewBox, you're repositioning or re-zooming the camera - the shapes stay put, but a different portion of the stage ends up on screen. When you change the viewport, you're swapping in a bigger or smaller monitor - same footage, different display size.
Left: shapes exist in the user coordinate system (the full stage). The dashed blue rectangle is the viewBox - it frames which part of the stage the camera captures. Right: the viewport - set by width/height - determines how large the result appears on screen.
These two sound almost identical, but they answer completely different questions:
| Viewport | viewBox | |
|---|---|---|
| Question it answers | How big is the SVG on the page? | What part of the canvas do we see? |
| Set by | width / height (or CSS) |
viewBox="min-x min-y w h" |
| Units | Real-world (px, em, %, vw…) | Abstract user units (no inherent physical size) |
| If you change it… | Picture gets larger/smaller on screen. Same content, different display size. | Different content becomes visible, or same content appears zoomed in/out. |
| Camera analogy | Size of the TV you watch on | Where the camera points + its zoom level |
The confusing part: they're both "boxes." But one is physical (how many CSS pixels on your page) and the other is logical (which rectangle of the infinite coordinate space gets mapped into that physical box). The viewport never changes the coordinates of your shapes. The viewBox redefines what coordinates are visible and how they scale.
Quick mnemonic: viewport = physical. viewBox = logical (the boundary of what's captured).
1. The Viewport
The viewport is the rectangular region the SVG occupies in the host document. You set it with width and height on the <svg> element:
<svg width="400" height="300">
<!-- content here -->
</svg>
Key points:
- Without units, these values are in CSS pixels (px).
- You can use any CSS unit:
em,rem,%,vw, etc. - If you omit
widthandheightentirely, the SVG defaults to300×150in most browsers (matching the CSS replaced element default). - The viewport defines the physical size on screen - not what coordinates you use inside.
Viewport vs. CSS sizing
You already know from HTML that you can override an element's intrinsic size with CSS. The same applies to SVG - width/height CSS properties override the attributes. This is important: the attribute defines the coordinate system aspect ratio, while CSS defines the actual rendered size.
2. The User Coordinate System (UCS)
Inside every SVG, there's a coordinate system where:
- The origin (0, 0) is at the top-left corner
- x increases rightward
- y increases downward (unlike maths, like screen coordinates)
- Each unit is, by default, one CSS pixel
The default SVG coordinate system. Origin top-left, y-axis pointing down.
- Cartesian: yes - two perpendicular axes, infinite in all directions. Negative coordinates are perfectly valid.
- Y-axis: inverted relative to maths. Positive y goes down the page (same convention as HTML/CSS and Canvas 2D - inherited from CRT scan order).
- Origin: (0, 0) at the top-left of the viewBox by default.
- Bounded: no. The plane is infinite. You can place shapes at any coordinate - the viewBox simply selects which rectangular portion is visible.
If your SVG is width="400" height="300" with no viewBox, then 1 user unit = 1 CSS pixel, and the coordinate space runs from (0,0) to (400,300).
3. The viewBox
This is where SVG gets powerful. The viewBox attribute redefines the internal coordinate system:
<svg width="400" height="300" viewBox="0 0 800 600">
<!-- coordinate system now runs 0-800 horizontally, 0-600 vertically -->
<!-- but it's all rendered into the 400x300 viewport -->
</svg>
The four values are: viewBox="min-x min-y width height"
- min-x, min-y - the top-left corner of the visible rectangle on the canvas
- width, height - how many user units are visible
viewBox is the camera's position and zoom level. A viewBox wider than the viewport zooms out (more content, smaller). A viewBox narrower than the viewport zooms in (less content, bigger). Shifting min-x/min-y pans the camera.
Why viewBox matters
- Resolution independence - your coordinates are abstract units, not pixels. The SVG scales to any size.
- Aspect ratio control - you define the ratio you want, independent of the container size.
- Panning - change min-x/min-y to slide the visible window across a larger canvas (think: maps, scrolling game worlds).
- Zoom - change width/height to zoom in/out without altering any child element coordinates.
Interactive: Adjusting the viewBox
Drag the sliders to pan and zoom the viewBox. The shapes never move - only the camera does.
4. preserveAspectRatio
What happens when the viewBox aspect ratio doesn't match the viewport? The preserveAspectRatio attribute controls this:
<svg width="400" height="200" viewBox="0 0 100 100"
preserveAspectRatio="xMidYMid meet">
The value has two parts:
- Alignment - where to place the viewBox within the viewport. Made of two tokens:
xMin,xMid,xMax(horizontal)YMin,YMid,YMax(vertical)
xMidYMid,xMinYMin, etc. - Meet or Slice:
meet- scale uniformly so the entire viewBox is visible (likeobject-fit: contain). Letterboxing may occur.slice- scale uniformly so the viewport is completely covered (likeobject-fit: cover). Parts of the viewBox may be clipped.
The special value none stretches non-uniformly to fill the viewport exactly - distorting the content. Rarely what you want.
object-fit and object-position, you already understand this. meet = contain, slice = cover, alignment = object-position. The SVG version predates the CSS version by over a decade.
Visual comparison: meet vs slice vs none
Same content (a circle, square, and triangle in a tall 100×300 viewBox) rendered into a square 200×200 viewport with different preserveAspectRatio values. The dashed border shows the viewport edge. With meet, the content is pillarboxed. With slice, the viewport is filled but content is cropped. With none, shapes are squashed to fit - the circle becomes an ellipse.
5. Nested Coordinate Systems
Every <svg> element - including nested ones - establishes a new viewport and coordinate system. This is how you can embed independent coordinate spaces within a larger SVG:
<svg width="400" height="300" viewBox="0 0 400 300">
<!-- outer coordinate system -->
<rect x="0" y="0" width="400" height="300" fill="#eee"/>
<!-- nested SVG with its own coordinate system -->
<svg x="50" y="50" width="200" height="150" viewBox="0 0 100 100">
<!-- inner coordinates: 0-100 in both axes -->
<circle cx="50" cy="50" r="40" fill="#306998"/>
</svg>
</svg>
The <g> element, by contrast, does not create a new coordinate system - it just groups elements and can apply transforms. Lesson 2 covers transforms in depth.
The outer SVG uses a 400×300 coordinate system (grey grid). The nested SVG (dashed blue border) has its own 100×100 coordinate system. The circle at inner (50,50) appears centred in the nested viewport, regardless of where that viewport sits in the outer space.
6. SVG Units and Coordinate Values
SVG supports length units: px, em, rem, ex, pt, pc, cm, mm, in, and %. In practice:
- Most SVG work uses unitless numbers (which default to the user unit, typically 1px).
- Percentages resolve relative to the viewport (or viewBox if set).
50%of x = half the viewBox width. - When no
viewBoxis set, 1 user unit = 1 CSS pixel. - When a
viewBoxis set, user units are abstract - their pixel size is computed from the viewBox-to-viewport ratio.
Left: no viewBox, so 1 user unit = 1 CSS pixel. A 50-unit square is 50px. Right: viewBox="0 0 400 400" mapped into a 200px viewport, so 1 user unit = 0.5 CSS pixels. The same 50-unit square renders at 25px.
7. Document Structure: <title>, <desc>, and <metadata>
Every SVG document can (and should) include descriptive elements that provide accessibility information and machine-readable metadata. These are invisible - they don't render anything - but they're important for screen readers, search engines, and tooling.
<svg viewBox="0 0 200 100" xmlns="http://www.w3.org/2000/svg"
role="img" aria-labelledby="svg-title svg-desc">
<title id="svg-title">Company Logo</title>
<desc id="svg-desc">A blue circle with the letter K inside,
representing the company brand.</desc>
<circle cx="100" cy="50" r="40" fill="#61afef"/>
<text x="100" y="62" text-anchor="middle"
font-size="32" fill="white">K</text>
</svg>
Hover over the circle - the <title> shows as a tooltip. Screen readers announce "Company Logo" as the accessible name. The <desc> provides a longer description.
| Element | Purpose | Equivalent in HTML |
|---|---|---|
<title> | Short accessible name. Shown as tooltip on hover. First child of <svg> or any element. | alt attribute on <img> |
<desc> | Longer description for assistive technology. Not displayed visually. | aria-describedby target |
<metadata> | Machine-readable metadata (RDF, Dublin Core, etc.). Ignored by rendering. | <meta> tags in <head> |
<title> and <desc> aren't just for the root <svg> - they can be the first child of any SVG element (a circle, a group, a path). This gives individual elements accessible names, which screen readers can announce when the user navigates interactive SVGs.
The width/height attributes set the viewport size. The viewBox maps a rectangle of user-space coordinates into that viewport. preserveAspectRatio controls how the mapping handles aspect ratio mismatches. Everything you draw uses the user coordinate system defined by the viewBox.
In practice, start every SVG with a viewBox that matches your intended design dimensions. Set width/height for the default display size, but rely on CSS for responsive sizing. This gives you resolution independence from the start, and every coordinate you write is meaningful in your design space rather than tied to a specific pixel count.
Quiz: Check Your Understanding
Question 1
If an SVG has width="200" height="200" viewBox="0 0 400 400", how large is one user unit on screen?
Question 2
What does changing the viewBox min-x value do?
Question 3
Which preserveAspectRatio value is equivalent to CSS object-fit: cover?
Hands-On Exercise
Create an SVG that demonstrates these concepts:
- Create an SVG with
width="300" height="150"andviewBox="0 0 600 300". - Draw a rectangle at
x="0" y="0" width="600" height="300"with a fill - it should fill the entire viewport despite being "600 units" wide. - Place a circle at
cx="300" cy="150"- it should appear dead centre. - Now change the viewBox to
"100 50 400 200"- predict what happens before you save. The camera "zooms in" and "pans" to show only the middle portion of the canvas.