# SVG gradients: linear, radial, strokes, text and masks

[Canonical HTML page](https://gradiently.design/guide/svg-gradients)

SVG has its own gradient system, older than CSS gradients and able to do things they cannot: colour a stroke, fill a word, share one gradient across many shapes, and fade anything with a mask.

## The short version

- An SVG gradient is defined once as a linearGradient or radialGradient element with stop children, given an id, and applied with fill or stroke set to url(#id).
- By default SVG gradient coordinates use objectBoundingBox units, where 0 and 1 are the edges of the shape being painted.
- gradientUnits="userSpaceOnUse" places the gradient in the drawing's own coordinates, which lets one gradient span several shapes and fixes gradients on perfectly straight lines.
- spreadMethod decides what happens beyond the gradient's ends: pad holds the end colours, reflect mirrors the blend and repeat restarts it.
- An SVG mask uses brightness, so a white to black gradient inside a mask fades whatever it is applied to.

An **SVG gradient** is a paint you define once and apply to any shape, line or piece of text in an SVG. You write a `<linearGradient>` or `<radialGradient>` element inside `<defs>`, give it `<stop>` children for its colours and an `id`, then set a shape's `fill` or `stroke` to `url(#id)`. Unlike a CSS background, it can colour a stroke or a single word, and it scales with the drawing.

```html
<svg viewBox="0 0 200 120" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <linearGradient id="dusk" x1="0" y1="0" x2="1" y2="1">
      <stop offset="0" stop-color="#1e1b4b" />
      <stop offset="0.55" stop-color="#7c3aed" />
      <stop offset="1" stop-color="#f472b6" />
    </linearGradient>
  </defs>
  <rect width="200" height="120" rx="16" fill="url(#dusk)" />
</svg>
```

A diagonal gradient on a rounded rectangle. x1, y1, x2 and y2 draw the gradient's line from top left to bottom right.

The same blend, as CSS: `linear-gradient(135deg, #1e1b4b 0%, #7c3aed 55%, #f472b6 100%)`

The SVG above and this CSS gradient match on a square. On a rectangle, the SVG version stretches with the shape; see gradientUnits below.

## linearGradient and its stops

`x1`, `y1`, `x2` and `y2` set the start and end of the gradient's line. Each `<stop>` takes an `offset` from 0 to 1 (or 0% to 100%), a `stop-color` and an optional `stop-opacity`. `stop-color` is also a CSS property, so you can theme gradients from a stylesheet, for example `stop { stop-color: var(--accent); }`.

| Attribute | Default | What it does |
| --- | --- | --- |
| `x1 y1 x2 y2` | `0 0 1 0`, left to right | Start and end points of the gradient line |
| `gradientUnits` | `objectBoundingBox` | Whether coordinates are relative to the shape or to the drawing |
| `spreadMethod` | `pad` | What happens beyond the ends: `pad`, `reflect` or `repeat` |
| `gradientTransform` | none | Rotate, scale or skew the gradient, e.g. `rotate(30)` |
| `href` | none | Inherit stops and settings from another gradient |

The main attributes of linearGradient. radialGradient shares all but the first row.

`href` saves repetition. Define your stops once in a base gradient, then create variations that point at it and change only the direction or units. The [MDN linearGradient reference](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/linearGradient) covers every attribute.

## radialGradient: centre, radius and focal point

A radial gradient takes a centre (`cx`, `cy`) and a radius (`r`), all defaulting to 0.5, so it fills the shape from the middle. It also has something CSS lacks: a separate focal point (`fx`, `fy`) where the first colour starts. Moving the focal point off-centre makes a sphere look lit from one side.

```html
<radialGradient id="orb" cx="0.5" cy="0.5" r="0.5" fx="0.32" fy="0.28">
  <stop offset="0" stop-color="#e0f2fe" />
  <stop offset="0.45" stop-color="#0ea5e9" />
  <stop offset="1" stop-color="#082f49" />
</radialGradient>

<circle cx="60" cy="60" r="50" fill="url(#orb)" />
```

A focal point up and to the left turns a flat circle into a glossy sphere.

Lit sphere, approximated in CSS: `radial-gradient(circle at 32% 28%, #e0f2fe, #0ea5e9 35%, #082f49 80%)`

CSS can move the centre but not split centre and focus, so this is only an approximation of the SVG orb. Compare [CSS radial gradients](https://gradiently.design/guide/css-radial-gradient).

## gradientUnits: objectBoundingBox or userSpaceOnUse

With the default `objectBoundingBox`, 0 and 1 mean the edges of whichever shape is being painted, so every shape gets the whole gradient, stretched to fit. With `userSpaceOnUse`, coordinates are in the SVG's own units, so the gradient stays put and shapes reveal whichever part of it they cover. That is how a row of bars can share one continuous gradient.

### objectBoundingBox

- Coordinates from 0 to 1 across each shape
- Every shape shows the full gradient
- The gradient stretches with the shape's proportions
- Fails on a perfectly horizontal or vertical line

### userSpaceOnUse

- Coordinates in the drawing's own units
- Shapes reveal their slice of one gradient
- Angles stay true whatever the shape
- Works on straight lines and thin strokes

> **The invisible straight line** A horizontal `<line>` has a bounding box with zero height, and bounding box units cannot be calculated from zero, so a default gradient stroke on it renders nothing. Set `gradientUnits="userSpaceOnUse"` and give real coordinates.

## Gradients on strokes and text

`stroke="url(#id)"` paints outlines, which is how gradient icons, progress rings and chart lines are drawn. On `<text>`, `fill="url(#id)"` colours the letters and the words stay selectable and searchable. Add a flat colour after the URL, such as `fill="url(#dusk) #7c3aed"`, and it is used if the gradient cannot be found.

```html
<svg viewBox="0 0 320 120" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <linearGradient id="line" gradientUnits="userSpaceOnUse" x1="20" y1="0" x2="300" y2="0">
      <stop offset="0" stop-color="#22d3ee" />
      <stop offset="1" stop-color="#a855f7" />
    </linearGradient>
  </defs>
  <path d="M20 90 C 90 20, 160 110, 300 30" fill="none" stroke="url(#line)" stroke-width="6" stroke-linecap="round" />
  <text x="20" y="115" font-size="20" font-weight="700" fill="url(#line) #7c3aed">Growth</text>
</svg>
```

One gradient in drawing units, shared by a curve and a word. Both run cyan to purple across the same horizontal span.

For gradient lettering in HTML rather than SVG, [gradient text in CSS](https://gradiently.design/guide/css-gradient-text) is usually simpler. Reach for SVG text when the words are part of an illustration or logo, or need to follow a path.

## spreadMethod: pad, reflect and repeat

When the gradient's line is shorter than the shape, `spreadMethod` decides what fills the rest. `pad` holds the end colours, `reflect` mirrors the blend back and forth, and `repeat` starts it again with a hard edge. Here each gradient runs from 30% to 70% of the width.

- pad: `linear-gradient(90deg, #0f766e 30%, #fde68a 70%)`
- reflect: `repeating-linear-gradient(90deg, #fde68a -10%, #0f766e 30%, #fde68a 70%)`
- repeat: `repeating-linear-gradient(90deg, #0f766e 30%, #fde68a 70%)`

Teal to sand with the three spread methods, reproduced in CSS so you can compare them side by side.

## Fading with SVG masks

A mask uses brightness: white areas show what is masked, black areas hide it, and greys are in between. So a white to black gradient inside a `<mask>` fades any shape or image smoothly to nothing, which is how you soften the edge of a photo or make a chart line fade out at its start.

```html
<defs>
  <linearGradient id="fade" x1="0" y1="0" x2="0" y2="1">
    <stop offset="0.4" stop-color="#fff" />
    <stop offset="1" stop-color="#000" />
  </linearGradient>
  <mask id="soft-bottom">
    <rect width="100%" height="100%" fill="url(#fade)" />
  </mask>
</defs>

<image href="photo.jpg" width="100%" height="100%" mask="url(#soft-bottom)" />
```

The photo is fully visible at the top and fades out over its lower 60%.

For HTML elements, CSS `mask-image: linear-gradient(#000 40%, transparent)` does the same job, using alpha rather than brightness. The mask technique also powers transparent [CSS gradient borders](https://gradiently.design/guide/css-gradient-border).

### Avoid

- Reusing the same gradient `id` in two inline SVGs on one page
- Hiding a sprite of gradient definitions with `display: none`
- Default units on straight lines
- Very long, smooth, dark gradients with no texture

### Do

- Unique ids, or one shared definitions sprite
- Hide sprites with zero width and height instead
- `userSpaceOnUse` for strokes and shared gradients
- Add grain to large fades, see [CSS noise texture](https://gradiently.design/guide/css-noise-texture)

## FAQ

### How do I add a gradient to an SVG?

Define a `<linearGradient>` or `<radialGradient>` with `<stop>` children and an `id` inside `<defs>`, then set a shape's `fill` or `stroke` to `url(#id)`.

### Why is my SVG gradient stroke not showing on a line?

A straight horizontal or vertical line has a zero-size bounding box, so the default `objectBoundingBox` units fail. Use `gradientUnits="userSpaceOnUse"` with real coordinates.

### How do I rotate an SVG gradient?

Change `x1`, `y1`, `x2` and `y2`, or add `gradientTransform="rotate(45 0.5 0.5)"` to rotate around the centre of the bounding box.

### Can I change SVG gradient colours with CSS?

Yes. `stop-color` and `stop-opacity` are CSS properties, so rules such as `stop { stop-color: var(--accent); }` work on inline SVG.

### Why did my SVG gradient disappear when I hid the SVG?

Definitions inside an SVG with `display: none` may not render. Hide the sprite with zero width and height, or absolute positioning, instead.
