145

I'm trying to get a gradient applied to an SVG rect element.

Currently, I'm using the fill attribute. In my CSS file:

rect {
    cursor: pointer;
    shape-rendering: crispEdges;
    fill: #a71a2e;
}

And the rect element has the correct fill color when viewed in the browser.

However, I'd like to know if I can apply a linear gradient to this element?

Hrishikesh Choudhari
  • 11,617
  • 18
  • 61
  • 74

6 Answers6

139

Just use in the CSS whatever you would use in a fill attribute. Of course, this requires that you have defined the linear gradient somewhere in your SVG.

Here is a complete example:

rect {
    cursor: pointer;
    shape-rendering: crispEdges;
    fill: url(#MyGradient);
}
<svg width="100" height="50" version="1.1" xmlns="http://www.w3.org/2000/svg">
      <style type="text/css">
        rect{fill:url(#MyGradient)}
      </style>
      <defs>
        <linearGradient id="MyGradient">
          <stop offset="5%" stop-color="#F60" />
          <stop offset="95%" stop-color="#FF6" />
        </linearGradient>
      </defs>
      
      <rect width="100" height="50"/>
    </svg>
morkro
  • 4,336
  • 5
  • 25
  • 35
Thomas W
  • 14,757
  • 6
  • 48
  • 67
  • 2
    So I created that gradient in a separate file, and used `fill` this way: `fill: url(../js/gradient.svg#MyGradient);`. Is this the right way? – Hrishikesh Choudhari Dec 27 '12 at 10:00
  • @HrishikeshChoudhari: Yes, this is correct, but Chrome and I think Safari as well don't support referencing elements from other files. Not sure about IE9 (can't test right now, just give it a try). – Thomas W Dec 27 '12 at 11:07
  • 75
    To anyone who reads this and asks "what about `fill: linear-gradient (...)`?" `fill` requires a [``](http://www.w3.org/TR/SVG/painting.html#SpecifyingPaint) which is built around the CSS2 `` class. In other words, this answer is currently the only way to do it via CSS at the time I am writing this comment. You need to add a `linearGradient` element. Lastly, going through the w3 Working Draft for [SVG2](http://www.w3.org/TR/SVG2/), it appears support for `linear-gradient` on the fill css rule has not and might not make it into the spec. – Arthur Weborg Mar 06 '15 at 20:20
  • How to change the direction in this case? – AGamePlayer Nov 20 '16 at 12:43
  • 1
    @AwQiruiGuo Have a look at [MDN](https://developer.mozilla.org/en/docs/Web/SVG/Element/linearGradient) (specifically the `gradientTransform` attribute) – Thomas W Nov 20 '16 at 19:33
  • Just to answer Marten's comment - this works fine in Chrome if the SVG is defined in the same file that you're intending using it from. It (apparently) does not work if you use the blah.svg#MyGradient notation that Hrishikesh mentioned. – Chris Rae Mar 28 '18 at 18:20
63

2019 Answer

With brand new css properties you can have even more flexibility with variables aka custom properties

.shape {
  width:500px;
  height:200px;
}

.shape .gradient-bg {
  fill: url(#header-shape-gradient) #fff;
}

#header-shape-gradient {
  --color-stop: #f12c06;
  --color-bot: #faed34;
}
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none" class="shape">
  <defs>
    <linearGradient id="header-shape-gradient" x2="0.35" y2="1">
        <stop offset="0%" stop-color="var(--color-stop)" />
        <stop offset="30%" stop-color="var(--color-stop)" />
        <stop offset="100%" stop-color="var(--color-bot)" />
      </linearGradient>
  </defs>
  <g>
    <polygon class="gradient-bg" points="0,0 100,0 0,66" />
  </g>
</svg>

Just set a named variable for each stop in gradient and then customize as you like in css. You can even change their values dynamically with javascript, like:

document.querySelector('#header-shape-gradient').style.setProperty('--color-stop', "#f5f7f9");
Community
  • 1
  • 1
Maciej Kwas
  • 6,169
  • 2
  • 27
  • 51
  • 3
    Not supported In IE. – aoakeson Apr 03 '19 at 17:37
  • 3
    @MaciejKwas, you are wrong. Old browsers don't stay forever so companies who are not ready now will be ready then. And if somebody isn't ready to discard a portion of his audience, it doesn't mean that he isn't ready for changes, it means he prefers to leverage the changes later to keep a bigger audience. – Finesse Aug 05 '19 at 13:53
  • 54
    @aoakeson IE is dead. End of lifed. Edge is also dying, this is a 2019 answer, so IE shouldn't count. IE can degrade gracefully by using a solid colour. – Ciprian Sep 16 '19 at 10:46
  • 10
    @aoakeson I'm amazingly surprised to come across that kind of response in 2019. You'd be naive as a developer to assume SVG support in IE on this level would _ever_ be supported, let alone a budding developer on SO giving you a bloated, polyfilled answer for something unnecessarily needed if you intend to support IE. – James Martin-Davies Jan 11 '20 at 23:25
35

Building on top of what Finesse wrote, here is a simpler way to target the svg and change it's gradient.

This is what you need to do:

  1. Assign classes to each color stop defined in the gradient element.
  2. Target the css and change the stop-color for each of those stops using plain classes.
  3. Win!

Some benefits of using classes instead of :nth-child is that it'll not be affected if you reorder your stops. Also, it makes the intent of each class clear - you'll be left wondering whether you needed a blue color on the first child or the second one.

I've tested it on all Chrome, Firefox and IE11:

.main-stop {
  stop-color: red;
}
.alt-stop {
  stop-color: green;
}
<svg class="green" width="100" height="50" version="1.1" xmlns="http://www.w3.org/2000/svg">
  <linearGradient id="gradient">
    <stop class="main-stop" offset="0%" />
    <stop class="alt-stop" offset="100%" />
  </linearGradient>
  <rect width="100" height="50" fill="url(#gradient)" />
</svg>

See an editable example here: https://jsbin.com/gabuvisuhe/edit?html,css,output

kumarharsh
  • 18,961
  • 8
  • 72
  • 100
  • The lack is that you don't know for sure what the stop class names are and what order they have. Actually, the solutions are the same good, the only difference is the CSS selectors. – Finesse Jul 01 '19 at 07:16
13

Here is a solution where you can add a gradient and change its colours using only CSS:

// JS is not required for the solution. It's used only for the interactive demo.
const svg = document.querySelector('svg');
document.querySelector('#greenButton').addEventListener('click', () => svg.setAttribute('class', 'green'));
document.querySelector('#redButton').addEventListener('click', () => svg.setAttribute('class', 'red'));
svg.green stop:nth-child(1) {
  stop-color: #60c50b;
}
svg.green stop:nth-child(2) {
  stop-color: #139a26;
}

svg.red stop:nth-child(1) {
  stop-color: #c84f31;
}
svg.red stop:nth-child(2) {
  stop-color: #dA3448;
}
<svg class="green" width="100" height="50" version="1.1" xmlns="http://www.w3.org/2000/svg">
  <linearGradient id="gradient">
    <stop offset="0%" />
    <stop offset="100%" />
  </linearGradient>
  <rect width="100" height="50" fill="url(#gradient)" />
</svg>

<br/>
<button id="greenButton">Green</button>
<button id="redButton">Red</button>
Finesse
  • 9,793
  • 7
  • 62
  • 92
7

Thank you everyone, for all your precise replys.

Using the svg in a shadow dom, I add the 3 linear gradients I need within the svg, inside a . I place the css fill rule on the web component and the inheritance od fill does the job.

    <svg viewbox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
      <path
        d="m258 0c-45 0-83 38-83 83 0 45 37 83 83 83 45 0 83-39 83-84 0-45-38-82-83-82zm-85 204c-13 0-24 10-24 23v48c0 13 11 23 24 23h23v119h-23c-13 0-24 11-24 24l-0 47c0 13 11 24 24 24h168c13 0 24-11 24-24l0-47c0-13-11-24-24-24h-21v-190c0-13-11-23-24-23h-123z"></path>
    </svg>
    
    <svg height="0" width="0">
      <defs>
        <linearGradient id="lgrad-p" gradientTransform="rotate(75)"><stop offset="45%" stop-color="#4169e1"></stop><stop offset="99%" stop-color="#c44764"></stop></linearGradient>
        <linearGradient id="lgrad-s" gradientTransform="rotate(75)"><stop offset="45%" stop-color="#ef3c3a"></stop><stop offset="99%" stop-color="#6d5eb7"></stop></linearGradient>
        <linearGradient id="lgrad-g" gradientTransform="rotate(75)"><stop offset="45%" stop-color="#585f74"></stop><stop offset="99%" stop-color="#b6bbc8"></stop></linearGradient>
      </defs>
    </svg>
    
    <div></div>

    <style>
      :first-child {
        height:150px;
        width:150px;
        fill:url(#lgrad-p) blue;
      }
      div{
        position:relative;
        width:150px;
        height:150px;
        fill:url(#lgrad-s) red;
      }
    </style>
    <script>
      const shadow = document.querySelector('div').attachShadow({mode: 'open'});
      shadow.innerHTML="<svg viewbox=\"0 0 512 512\">\
        <path d=\"m258 0c-45 0-83 38-83 83 0 45 37 83 83 83 45 0 83-39 83-84 0-45-38-82-83-82zm-85 204c-13 0-24 10-24 23v48c0 13 11 23 24 23h23v119h-23c-13 0-24 11-24 24l-0 47c0 13 11 24 24 24h168c13 0 24-11 24-24l0-47c0-13-11-24-24-24h-21v-190c0-13-11-23-24-23h-123z\"></path>\
      </svg>\
      <svg height=\"0\">\
      <defs>\
        <linearGradient id=\"lgrad-s\" gradientTransform=\"rotate(75)\"><stop offset=\"45%\" stop-color=\"#ef3c3a\"></stop><stop offset=\"99%\" stop-color=\"#6d5eb7\"></stop></linearGradient>\
        <linearGradient id=\"lgrad-g\" gradientTransform=\"rotate(75)\"><stop offset=\"45%\" stop-color=\"#585f74\"></stop><stop offset=\"99%\" stop-color=\"#b6bbc8\"></stop></linearGradient>\
      </defs>\
    </svg>\
    ";
    </script>

The first one is normal SVG, the second one is inside a shadow dom.

GirkovArpa
  • 4,427
  • 4
  • 14
  • 43
Roland Gautier
  • 345
  • 3
  • 6
-2

Here is how to set a linearGradient on a target element:

<style type="text/css">
    path{fill:url('#MyGradient')}
</style>
<defs>
    <linearGradient id="MyGradient">
        <stop offset="0%" stop-color="#e4e4e3" ></stop>
        <stop offset="80%" stop-color="#fff" ></stop>
    </linearGradient>
</defs>