Topic 1.4
viewBox and preserveAspectRatio
In one line
The viewBox attribute gives an SVG its own coordinate window, so the drawing scales to whatever size the screen gives it. preserveAspectRatio decides what happens when window and screen have different shapes: fit, fill, or stretch.
Think of it like this
A slide in a projector. The slide never changes, but you can project it onto a postcard-sized screen or a wall. The picture grows or shrinks to fill the screen, and nothing needs to be redrawn. A viewBox is the slide: a fixed set of coordinates that the browser stretches onto any screen size.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- viewBox
- Four numbers (min-x, min-y, width, height) that say which part of the drawing's coordinates to show. The browser stretches that window onto the SVG's screen size.
- scale
- How many screen pixels one user unit becomes. A 200-wide viewBox shown in 300 pixels has scale 1.5.
- aspect ratio
- Width divided by height. A 200 by 100 box has ratio 2.
- meet
- Fit the whole viewBox inside the box, keeping its shape. Empty bars may remain.
- slice
- Cover the whole box, keeping the shape. Parts of the drawing may be cropped.
- letterbox
- The empty bars above and below (or left and right) of a picture that is fitted without stretching.
- responsive
- A layout that changes with the screen size. Here: a drawing that scales to the width it is given.
Step by step
01See the same drawing at four viewBoxes
The first example draws one small scene (a frame, a circle and a word) in four 200 by 100 SVGs. The only thing that changes is the viewBox. With none, one unit is one pixel. With 0 0 100 50 you look at the top-left quarter of the scene, so it fills the box: a zoom of 2. With 50 25 100 50 the same zoom is moved to the middle: a pan. With 0 0 400 200 the window is twice as big as the scene, so everything is half size.
For each SVG the Console prints the scale and offset the browser used, read from the circle's getCTM() (its current transformation matrix: where user units land on screen).
02Fit, fill or stretch
The second example puts one square drawing (viewBox 0 0 100 100) in three 300 by 100 boxes. meet shows the whole square at scale 1, centred, with 100 pixels of empty space on each side. slice scales by 3 to fill the width, so it crops the top and bottom: only the middle third shows. none stretches x by 3 and keeps y at 1: the circle becomes a wide oval.
Below the pictures the Console lists what the nine alignments do. In a wide box with a square viewBox, xMin, xMid and xMax push the picture to the left, centre or right (offsets 0, 100 and 200). The Y words do nothing here because the picture already fills the height. In a tall box it is the other way round.
<svg width="300" height="100" viewBox="0 0 100 100"> <!-- meet, the default -->
<svg width="300" height="100" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid slice">
<svg width="300" height="100" viewBox="0 0 100 100" preserveAspectRatio="none">03Make the width follow the box
The third example puts width="100%" SVGs in boxes 160, 240 and 320 pixels wide. With viewBox="0 0 200 100" and no height, the SVG is 80, 120 and 160 tall, always half its width, and the scale is 0.8, 1.2 and 1.6. Without a viewBox, the SVG is always 150 tall and nothing scales: the picture would be cut off or squashed.
Using the Phase 0 week chart, a viewBox of 0 0 380 210 on an SVG with width="100%" fits it to any box, and the code inside does not change at all. Topic 1.7 does exactly this.
04When the viewBox itself is wrong
The same example ends with broken viewBoxes. NaN prints Error: <svg> attribute viewBox: Expected number, "0 0 NaN 100". A width of 0 draws nothing and prints nothing: the SVG is blank. A negative width and a viewBox with only three numbers are ignored, so the drawing appears at scale 1 as if there were no viewBox. The Console prints A negative value is not valid. for the first and Unexpected end of attribute. Expected number for the second. Zero width is the one with no message anywhere.
A blank or unscaled chart with no error is the usual sign. Check the viewBox's four numbers first.
Try it yourself
- 1
Zoom in on the circle
In the first example, change the second viewBox to
15 20 60 30. Now the circle fills most of the box. The second line of the Console prints a scale of 3.33…: a 60-wide window in 200 pixels. Zoom is only a smaller window. - 2
Slice instead of meet
In the second example's first call, change
""to"xMinYMin slice". The picture is cropped and pinned to the top-left: you see the top third of the square, with the L in the corner. Pinning is what the Min and Max words are for. - 3
Drop the height
In the third example, change
'<svg width="100%" 'to'<svg height="60" '. Now every SVG with a viewBox is 120 by 60, scale 0.6, whatever its box: the browser takes the width from the viewBox's shape. The one without a viewBox becomes 300 by 60. Give one dimension, not both, when the shape should follow the viewBox.
Code & diagrams
Zoom is a smaller viewBox, pan is a different start. The numbers are the browser's own, read back from the drawing.
Preview
Console
Nothing logged yet.Expected console output
viewBox no viewBox -> scale 1, moved by 0, 0
viewBox 0 0 100 50 -> scale 2, moved by 0, 0
viewBox 50 25 100 50 -> scale 2, moved by -100, -50
viewBox 0 0 400 200 -> scale 0.5, moved by 0, 0A square drawing in a 3-to-1 box. Meet fits it whole, slice crops it, none distorts it. The last two lines show where the nine alignments place it.
Preview
Console
Nothing logged yet.Expected console output
meet (the default): scale 1.0 across, 1.0 down, offset 100, 0
slice, centred: scale 3.0 across, 3.0 down, offset 0, -100
none, stretched: scale 3.0 across, 1.0 down, offset 0, 0
wide box, xMin / xMid / xMax (YMid): 0,0 100,0 200,0
tall box, YMin / YMid / YMax (xMid): 0,0 0,100 0,200With a viewBox the height follows the width. The zero-width viewBox draws nothing (look at the blank box in the preview); the negative and three-number ones print their errors and show scale 1 because the browser ignored them.
Preview
Console
Nothing logged yet.Expected console output
viewBox 0 0 200 100 in a 160px box: SVG is 160 x 80, scale 0.8
viewBox 0 0 200 100 in a 240px box: SVG is 240 x 120, scale 1.2
viewBox 0 0 200 100 in a 320px box: SVG is 320 x 160, scale 1.6
no viewBox in a 240px box: SVG is 240 x 150, scale 1
zero width (blank) in a 240px box: SVG is 240 x 150, scale 1
negative width in a 240px box: SVG is 240 x 150, scale 1
Error: <svg> attribute viewBox: A negative value is not valid. ("0 0 -200 100")
three numbers only in a 240px box: SVG is 240 x 150, scale 1
Error: <svg> attribute viewBox: Unexpected end of attribute. Expected number, "0 0 200".
NaN in a 240px box: SVG is 240 x 150, scale 1
Error: <svg> attribute viewBox: Expected number, "0 0 NaN 100".Try it: interactive
The viewBox lab
Change the four viewBox numbers and the alignment, and watch the window move over the drawing. Try a zoom, a pan, and a box with a different shape.
Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Squash a drawing with preserveAspectRatio="none"
In the third example's first inBox call, change the SVG attributes to viewBox="0 0 200 100" height="150" preserveAspectRatio="none".
Break #2
Type a viewBox with NaN
In your own page, compute the viewBox from data that is not ready yet: svg.setAttribute("viewBox", "0 0 " + width + " 100") where width is NaN.
Myth vs fact
Myth
The viewBox is the size of the SVG.
Fact
The SVG's size is its width and height (or CSS). The viewBox is the part of the drawing's own coordinates that fills that size. They are separate, and the scale is the ratio between them.
Myth
Scaling an SVG only changes its size, so the lines stay the same thickness.
Fact
Strokes and text scale with the drawing. A 2-unit stroke at scale 3 is 6 pixels thick. CSS vector-effect: non-scaling-stroke keeps a stroke's width in screen pixels.
Myth
An SVG always fills its box if I give it width 100%.
Fact
Only with a viewBox. Without one the height falls back to 150 pixels and nothing scales.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
A viewBox keeps the drawing's coordinates stable, but it also shrinks text. A chart drawn for 380 units wide and shown in 190 pixels has text half its size. For text that must stay readable, responsive charts measure their container and redraw with new coordinates (Phase 10). A viewBox is the right tool for icons, fixed-shape illustrations and the simple dashboard frame built here.
- ▸
getCTM()returns the matrix[a b c d e f]that maps an element's units to its nearest SVG's pixels:aanddare the horizontal and vertical scale,eandfthe offset.getScreenCTM()goes all the way to the page, which tooltips and pointer maths need (Phase 8). - ▸
Because
slicecrops, a 3-to-1 box with a square viewBox shows only the middle third by default (YMid). Anything the viewBox says is visible can be outside the box. Usemeetfor charts and diagrams (nothing must be cut) andslicefor decorative backgrounds. - ▸
When both
widthandheightcome from CSS (for examplewidth: 100%; height: 100%in a sized parent), the box's shape is whatever the layout gives it. A mismatch with the viewBox then letterboxes the drawing (meet) rather than squashing it, which is whynoneis rarely right for charts.
Remember this
- 1
The problem this solves: the Phase 0 week chart has a fixed width of 380 pixels. On a phone it overflows, so the box scrolls sideways. On the wall screen it stays small. With a
viewBoxthe same drawing scales to the space it is given, from a phone to a wall, without changing a single coordinate inside it. - 2
The four numbers.
viewBox="min-x min-y width height"says which part of the drawing's own coordinates to show.viewBox="0 0 200 100"shows x from 0 to 200 and y from 0 to 100. Thewidthandheightattributes (or CSS) say how big the SVG is on the screen. The browser then scales byscreen width ÷ viewBox width. A viewBox 200 wide shown in 400 pixels doubles everything. - 3
User units and pixels are now different things. Until now one unit was one pixel. With a viewBox, one unit is whatever the scale makes it: half a pixel, two pixels, or 1.53. Stroke widths and text sizes scale with the drawing, too. A 2-unit line in a viewBox shown at three times its size is a 6-pixel line.
- 4
Zoom and pan are just edits to the viewBox. A smaller viewBox shows less of the drawing, so it looks bigger: that is zoom. Changing
min-xandmin-ymoves the window: that is pan. Map and chart zoom later in this course (Phase 8) change exactly these four numbers. - 5
**
preserveAspectRatiohandles the mismatch.** If the viewBox is square but the screen box is wide, something has to give.meet(the default) fits the whole drawing inside, leaving empty bars, likebackground-size: contain.slicefills the box and crops the overflow, likecover.nonestretches each direction on its own, which distorts circles into ovals. The first word picks where the drawing sits:xMin,xMidorxMax, thenYMin,YMidorYMax, giving 9 alignments. The default isxMidYMid meet. - 6
A responsive SVG has a viewBox and no fixed height. Give the SVG
width="100%"and aviewBox, and leave outheight: the browser works out the height from the viewBox's shape. Without a viewBox the height falls back to 150 pixels and nothing scales, which is the usual cause of a squashed or cut-off chart. A wrong viewBox fails differently depending on the mistake:NaNprints an error, a zero width draws nothing, and a negative or incomplete value is ignored, all described in the third example.
Explain it without notes
What do the four numbers of a viewBox mean, and how does the browser use them?
What is the difference between meet and slice?
Why does an SVG with width="100%" and no viewBox stay 150 pixels tall?
What do you see when a viewBox is 0 0 0 100, 0 0 -200 100 and 0 0 NaN 100?
Practice
A drawing is 380 wide and 210 tall (as in the Phase 0 week chart). Write the opening <svg> tag so it fills its container's width and keeps the right shape.
You want to zoom into the area from x = 100 to x = 200 and y = 20 to y = 70 of a drawing. What viewBox do you write, and what is the scale if the SVG is 400 pixels wide?
Trade-offs
- ↔
A viewBox gives free scaling and crisp lines at any size, but text scales too and may become too small on a phone. Redrawing at the container's size keeps text readable at the cost of more code (Phase 10).
- ↔
slicemakes a drawing always fill its box but may crop data. For charts, cropped data is a lie by omission, so charts usemeet.
Done when you can
I can say what each of the four viewBox numbers does.
I can compute the scale from a viewBox and an SVG size.
I know when to use meet, slice and none, and why charts use meet.
I can make an SVG responsive: width 100%, a viewBox, no height.
I can recognise a NaN, zero, negative or incomplete viewBox from what I see.