Command Palette

Search for a command to run...

Hectal
PHASE 1Beginner ~26 min· topic 6 of 7

Topic 1.6

Groups, Transforms, Defs and Clipping

In one line

A <g> groups shapes so they move and style together. <defs>, <use> and <symbol> define a drawing once and reuse it. Gradients and patterns fill shapes, and clip paths and masks cut them, while the hidden parts stay in the page.

Think of it like this

A transparent folder of tracing paper. Draw several shapes on one sheet, slide the sheet across the table, and every shape moves together. Write "use blue ink" on the sheet, and every shape without its own ink follows. A <g> is that sheet.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

group (<g>)
An invisible container. Its style is inherited by its children, and its transform moves all of them.
transform
A change of position, turn or size applied to an element and everything inside it.
inherit
To take a value from the parent unless you set your own. fill and font-size are inherited.
defs
A place for definitions (symbols, gradients, patterns, clip paths) that are used elsewhere and not drawn on their own.
symbol
A reusable drawing with its own viewBox. It is drawn only through <use>.
gradient stop
A colour at a position (offset 0 to 1) along a gradient.
clip path
An outline that hides everything outside it. Hard edge.
mask
A greyscale picture that hides or fades what is under it: white shows, black hides.

Step by step

01Move a whole group at once

In the first example, three bars and a baseline sit inside <g transform="translate(20 30)" fill="#2a9d8f">. Their own x values are 0, 40 and 80. The group moves them all 20 across and 30 down, and the Console shows the first bar really lands at x = 20 on the page.

The group's fill is inherited by the bars that do not set their own. The middle bar sets fill="#e76f51" and keeps it. The Console prints the computed fill of each bar to prove it.

a group sets position and shared stylewhole filehtml
<g transform="translate(20 30)" fill="#2a9d8f">
  <rect x="0"  y="40" width="30" height="60"></rect>
  <rect x="40" y="10" width="30" height="90" fill="#e76f51"></rect>   <!-- its own fill wins -->
  <rect x="80" y="30" width="30" height="70"></rect>
</g>

02Order of transforms changes the place

On the right of the first example are two identical rectangles with a dot at their own corner (0, 0). The first group is translate(300 20) rotate(30): the corner lands exactly at (300, 20). The second is rotate(30) translate(300 20): the move happens in the turned system, so the corner lands at (249.8, 167.3).

The Console prints each group's matrix. The six numbers a b c d e f are how the browser stores any transform: e and f are where the origin lands, and a, b, c, d hold the turn and stretch (0.87 and 0.5 are the cosine and sine of 30 degrees).

03Define a pin once and draw it three times

In the second example, <defs> holds one <symbol id="pin">: a station marker with a path and a small circle. Three <use> lines draw it at three sizes and in three colours. The path data is written once.

The colour works because the pin's shapes set no fill of their own, so they inherit the fill of each <use>. A shape with its own fill would ignore it.

define once, use many timeswhole filehtml
<defs>
  <symbol id="pin" viewBox="0 0 24 24"><path d="M12 2 C8 2 5 5 5 9 ...Z"></path></symbol>
</defs>
<use href="#pin" x="10"  y="10" width="24" height="24" fill="#e76f51"></use>
<use href="#pin" x="50"  y="10" width="36" height="36" fill="#2a9d8f"></use>

04Ramps, hatches and the horizontal-line trap

Below the pins is a gradient legend (light to dark teal, like a map's rides legend), and a hatched box for "no data". Two thick lines use the gradient as a stroke. The perfectly horizontal one is invisible: its box has zero height, and a gradient measured against that box has nothing to paint. The second line, two units off horizontal, shows the gradient.

05Clip and mask, and count what is left

At the bottom, 40 bars are drawn, but a clip path shows only the part inside a 100-unit-wide window. The Console counts: 40 bars exist, 10 are visible. A mask fades a bar from solid to see-through.

The last lines of the Console show the two silent failures: a <use> that points to a missing id has a 0 by 0 box and no error, and a <symbol> used without a width and height grows to fill the whole drawing (it is measured in a separate, temporary SVG).

Try it yourself

  1. 1

    Swap the transform order

    In the first example, change the first group to rotate(30) translate(300 20). Its dot jumps to (249.8, 167.3), exactly where the second group's dot is, and the two rectangles overlap. Same two transforms, different order, different place.

  2. 2

    Rotate about the middle

    Change the first group to translate(300 20) rotate(30 30 12). The rectangle now turns about its own centre (30, 12), which stays at (330, 32), instead of about its corner. The corner dot moves to (310.0, 6.6) because the whole rectangle is turned around its middle.

  3. 3

    Give the pins a size

    In the second example, delete width="24" height="24" from the first <use>. The pin is no longer 24 by 24: it grows to fill the SVG, as large as its 330-unit height allows, and covers the other shapes. A <symbol> takes its size from the <use>.

Code & diagrams

Groups, inheritance and the order of transforms New tab

Both groups contain the same rectangle and dot. Only the order of translate and rotate differs, and the dot ends up in two different places.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

Bar 1 fill (inherited from the group): rgb(42, 157, 143)
Bar 2 fill (its own):                  rgb(231, 111, 81)
Bar 1 starts at x = 0, but lands on the page at x = 20
first  group: [0.87 0.50 -0.50 0.87 300.00 20.00], origin lands at (300.0, 20.0)
second group: [0.87 0.50 -0.50 0.87 249.81 167.32], origin lands at (249.8, 167.3)
Define once, reuse, fill, clip and mask New tab

Look at the picture as well: the clipped bars end at the dashed window, and the flat gradient line is missing on purpose.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

Bars in the page: 40, visible through the clip: 10
The clipped group still measures 398 units wide
Uses of the symbol: 3 (the path is written once)
A symbol used with no width and height measures 200 x 200 in a 300 x 200 SVG
A use of a missing id measures 0 x 0 (and prints no error)

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

Point a use at the wrong id

In the second example, change the first <use href="#pin" to <use href="#pins" (one letter more).

terminal
$ Preview and Console
── what you'll see ──
The first pin disappears and the other two remain. The Console prints `Uses of the symbol: 3` as before: the element is still there, but it points at nothing. No error is printed.

Break #2

A gradient on a perfectly flat line

In the second example, change y2="177" to y2="175" on the second line.

terminal
$ Preview
── what you'll see ──
Both lines are now missing: the second line is perfectly horizontal too. No error is printed.

Myth vs fact

Myth

Clipping something out removes it, so it costs nothing.

Fact

The clipped parts are still in the page and still measured. The example has 40 bars in the page and 10 visible. Remove or never create what the learner cannot see.

Myth

<use> copies the symbol's code into the page.

Fact

It references one definition. Change the definition and every use changes. The copies live only in the browser's rendering.

Myth

The order of transforms does not matter.

Fact

It decides where the shape ends up. translate(300 20) rotate(30) and rotate(30) translate(300 20) put the same corner at (300, 20) and (249.8, 167.3).

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    Transform lists are applied right to left to the shape, equivalently left to right to the coordinate system. It helps to read translate(100 50) rotate(30) as: "move the origin to (100, 50), then draw everything turned by 30 degrees".

  • ▸

    scale(1 -1) flips an SVG so that y points up, as in school maths, but it flips text upside down too. That is why chart code subtracts from a baseline (Topic 3.2), or lets a scale flip the range (Phase 6), instead of flipping the whole chart.

  • ▸

    A <use> instance is not its own DOM tree you can style freely: CSS and events reach it through a shadow-like tree. For hundreds of data-driven marks, create real shapes from data (Phase 5) and use <use> for static icons like station pins.

  • ▸

    Gradients, patterns, clip paths and masks cost extra work per frame. Hundreds of unique gradients on thousands of shapes can make a page slow. Share one definition, and for large data move to Canvas (Phase 11, 12).

Remember this

  1. 1

    The problem this solves: a chart has hundreds of marks that must move to the plot area, share a colour, or repeat 25 times (one icon per station). Without groups and reuse you would repeat numbers and path data everywhere, and one change would need hundreds of edits.

  2. 2

    **<g> groups, and children inherit.** A <g> draws nothing itself. Styles such as fill, stroke and font-size set on it pass down to every child, unless a child sets its own. A transform on the group moves the children's whole coordinate system: a child at x="0" in a group with translate(20 30) is drawn at x = 20. Charts use exactly this for the margin convention: one <g transform="translate(left, top)"> for the plot area (Topic 3.2).

  3. 3

    Transforms: translate, rotate, scale. translate(x y) slides, rotate(angle) turns by degrees about the origin (clockwise on screen, because y points down), and scale(sx sy) stretches. rotate(30 50 50) turns about the point (50, 50) instead. Order matters: a transform list is applied from the right. translate(100 50) rotate(30) first turns the shape about its own corner and then moves it, so the shape's (0, 0) lands at (100, 50). rotate(30) translate(100 50) moves by (100, 50) in an already turned system, so (0, 0) lands somewhere else. The first example prints both.

  4. 4

    **<defs>, <use> and <symbol>: define once, draw many times.** Anything inside <defs> is not drawn directly. A <use href="#pin"> draws a copy of the element with that id. A <symbol> is a reusable drawing with its own viewBox (Topic 1.4), so each <use> can give it any x, y, width and height. Without a width and height, the symbol fills the whole SVG, which is a common surprise. A <use> that points to a missing id draws nothing and prints no error. Ids must be unique on the page: with duplicates, the first one wins.

  5. 5

    Gradients and patterns fill shapes. A <linearGradient> lists <stop> colours at offsets from 0 to 1; use it with fill="url(#ramp)". Colour legends for charts are a gradient in a rectangle. A <pattern> repeats a small tile, such as diagonal stripes. A hatched fill for "no data" works for people who cannot tell the colours apart. A gradient's default size is the shape's own box, so a perfectly horizontal or vertical line (a box with zero height or width) gets no gradient and is not drawn at all.

  6. 6

    Clip paths and masks cut shapes; the hidden part stays. A <clipPath> cuts a shape to an outline with a hard edge. A <mask> uses brightness (white shows, black hides) for soft edges and fades. Both only affect painting. A clipped-out bar is still in the page: still counted, still measured (getBBox() reports its full size), still handled by the browser for layout and events. Clipping 10,000 off-screen marks to a small window therefore costs almost the same as drawing them. The second example counts 40 bars with only 10 showing.

Explain it without notes

01

What do the children of a <g> inherit, and what does a transform on the group do to them?

02

Why does translate(300 20) rotate(30) put a shape in a different place from rotate(30) translate(300 20)?

03

What is the difference between <symbol> and <g>, and what happens if a <use> of a symbol has no width and height?

04

A clip path hides 30 of 40 bars. How many bars are still in the page, and why does that matter?

Practice

01

Write a <g> that moves three circles 50 across and 40 down, and makes all of them #2a9d8f except one that is #e76f51. What are the page coordinates of a circle with cx="10" cy="10" inside it?

02

Write a <defs> with one <symbol id="dot" viewBox="0 0 10 10"> holding a circle, and two <use>s that draw it at 20 by 20 and 40 by 40.

Trade-offs

  • ↔

    <use> keeps the page small and consistent, but each copy is harder to style on its own. Real shapes cost more nodes but are easy to change one by one.

  • ↔

    A mask gives a soft fade where a clip path gives a hard edge, at a higher drawing cost. Use a clip path unless you need the fade.

Done when you can

  • I can group shapes and give the group a style and a transform.

  • I can predict where a shape lands from a transform list, and I know the order matters.

  • I can define a symbol once and use it many times at different sizes.

  • I know why a clipped-out mark still costs, and why a flat line shows no gradient.