skip to Main Content

I’m trying to convert this simple SVG gradient to CSS:

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="Layer_2" data-name="Layer 2" width="107.097" height="26.024" viewBox="0 0 107.097 26.024">
  <defs>
    <style>
      .cls-1 {
        fill: url(#linear-gradient);
        stroke-width: 0px;
      }
    </style>
    <linearGradient id="linear-gradient" x1="795.61" y1="-6.181" x2="707.657" y2="35.941" gradientTransform="translate(-701.986 26.024) scale(1 -1)" gradientUnits="userSpaceOnUse">
      <stop offset="0" stop-color="#f0ce45" stop-opacity="0"/>
      <stop offset="1" stop-color="#fd00ac"/>
    </linearGradient>
  </defs>
  <g id="Layer_1-2" data-name="Layer 1">
    <rect class="cls-1" x="0" y="0" width="107.097" height="26.024" transform="translate(107.097 26.024) rotate(-180)"/>
  </g>
</svg>

But I can’t find the equivalent of stop-opacity in CSS. From MDN docs:

The stop-opacity attribute defines the opacity of a given color
gradient stop.

The opacity value used for the gradient calculation is the product of
the value of stop-opacity and the opacity of the value of the
stop-color attribute. For stop-color values that don’t include
explicit opacity information, the opacity is treated as 1.

There is a stop-opacity property in CSSm but it doesn’t behave the same.

.gradient {
  width: 200px;
  height: 20px;
  stop-opacity: 0;
  background-image: linear-gradient(to left, #f1109b, #f0ce45)
}
<div class="gradient">
</div>

How can I recreate that gradient in CSS?

2

Answers


  1. You can use a mask-image and apply a gradient as the image. However keep in mind that it is not fully supported everywhere yet.

    .gradient {
      width: 200px;
      height: 20px;
      background-image: linear-gradient(to left, #f1109b, #f0ce45);
      
      -webkit-mask-image: linear-gradient(to left, rgba(0, 0, 0, 1), rgba(0, 0, 0, 0));
      mask-image:         linear-gradient(to left, rgba(0, 0, 0, 1), rgba(0, 0, 0, 0));
    }
    <div class="gradient"></div>
    Login or Signup to reply.
  2. This question sent me into some kind of rabbit hole of re-learning CSS and SVG (or correcting my false beliefs thereof) and I’d like to preserve a memento here for future reference. Same as @enxameta in comments, I thought that translating such simple linear gradient from SVG to CSS must be quite straightforward. There are two problems that prevents it to be … simply doable:

    1: Hue of fully transparent colour does not affect CSS gradient

    Honestly, I would have sworn that linear-gradient(#FFF, #FFF0) must be different from linear-gradient(#FFF, #0000), but, in CSS gradients, it is not.

    two identical rectangles filled with vertical white-to-black gradient

    Neither in animations.

    div {
     text-align: center;
    }
    .bg ~ div {
     background-image: linear-gradient(to right, var(--c1), var(--c2));
    }
    .anim ~ div {
     background-image: none;
     animation: 1s a infinite alternate linear;
    }
    @keyframes a {
     from { background-color: var(--c1); }
     to { background-color: var(--c2); }
    }
    html {
     background-color: #000;
     color: snow;
    }
    <p>To transparent white:
    <span style="background-image: linear-gradient(#FFF, #FFF0)">&nbsp; &nbsp;</span>
    <span style="background-image: linear-gradient(#FFF, #0000)">&nbsp; &nbsp;</span>
    :to transparent black.
    
    
    <p class="bg">CSS Gradients from fully opaque red…
    <div style="--c1: #F00F; --c2: #0000;">…to fully transparent black</div>
    <div style="--c1: #F00F; --c2: #FFF0;">…to fully transparent white</div>
    <div style="--c1: #F00F; --c2: #F000;">…to fully transparent red</div>
    <div style="--c1: #F00F; --c2: #0005;">…to almost transparent black</div>
    <div style="--c1: #F00F; --c2: #FFF5;">…to almost transparent white</div>
    <div style="--c1: #F00F; --c2: #F005;">…to almost transparent red</div>
    <p class="anim">CSS Animations from fully opaque red…
    <div style="--c1: #F00F; --c2: #0000;">…to fully transparent black</div>
    <div style="--c1: #F00F; --c2: #FFF0;">…to fully transparent white</div>
    <div style="--c1: #F00F; --c2: #F000;">…to fully transparent red</div>
    <div style="--c1: #F00F; --c2: #0005;">…to almost transparent black</div>
    <div style="--c1: #F00F; --c2: #FFF5;">…to almost transparent white</div>
    <div style="--c1: #F00F; --c2: #F005;">…to almost transparent red</div>

    Yes, it makes very little sense, especially compared to SVG, where gradient stops with stop-opacity="0" (or with colours of zero alpha) affect its surroundings. Another example, comparing SVG and CSS gradient:

    svg basically shows two hues blending, even if the second one is transparent in the end

    2: CSS gradients are positioned differently than SVG

    In CSS "0%"/"100%" stop is the tangent that crosses closest/farthest corner and is perpendicular to angled gradient axis. Is SVG you basically throw a line segment somewhere and gradient lines are perpendicular to only that starting and ending on x1 y1 – x2 y2. So translating any non-orthogonal gradient could be also a challenge, since you have to re-compute and last offset stops with regard to dimensions (ratio) of target element.

    two rectangles with similar gradients, but sadly not identical


    Some garbaj produced along the way, for making illustrations above.

    CSS VS SVG gradients:

    :root { color-scheme: dark;}
    div { clear: left; }
    svg,p { float: left; margin: 0; }
    <div>From 1 to 0</div>
    
    <div> SVG with RGB #... + stop-opacity 1 0
    <svg width="200" height="50" viewBox="0 0 200 50">
      <defs>
        <linearGradient id="g-1" x2="200" gradientUnits="userSpaceOnUse">
          <stop offset="0" stop-color="#0F0" stop-opacity="1"/>
          <stop offset="1" stop-color="#F0F" stop-opacity="0"/>
        </linearGradient>
      </defs>
      <rect fill="url(#g-1)" width="100%" height="100%" />
    </svg>
    </div>
    <div> SVG with RGBA rgba #...F #...0
    <svg width="200" height="50" viewBox="0 0 200 50">
      <defs>
        <linearGradient id="g0" x2="200" gradientUnits="userSpaceOnUse"  color-interpolation="sRGB">
          <stop offset="0" stop-color="#0F0F"/>
          <stop offset="1" stop-color="#F0F0"/>
        </linearGradient>
      </defs>
      <rect fill="url(#g0)" width="100%" height="100%" />
    </svg>
    </div>
    <div>
    CSS with rgba #...F #...0
    <p style="width: 200px; height: 50px; 
    background-image: linear-gradient(to right, #0F0F, #F0F0)
    "></p>
    </div>
    <div>
    CSS with rgba #...F #FFF0 (!!!)
    <p style="width: 200px; height: 50px; 
    background-image: linear-gradient(to right, #0F0F, #FFF0)
    "></p>
    <div>
    CSS with rgba #...F #0000 (!!!)
    <p style="width: 200px; height: 50px; 
    background-image: linear-gradient(to right, #0F0F, #0000)
    "></p>
    </div>
    
    
    
    <div>From 0 to 1</div>
    
    <div> SVG with RGB #... + stop-opacity 0 1
    <svg width="200" height="50" viewBox="0 0 200 50">
      <defs>
        <linearGradient id="g1" x2="200" gradientUnits="userSpaceOnUse">
          <stop offset="0" stop-color="#0F0" stop-opacity="0"/>
          <stop offset="1" stop-color="#F0F" stop-opacity="1"/>
        </linearGradient>
      </defs>
      <rect fill="url(#g1)" width="100%" height="100%" />
    </svg>
    </div>
    <div> SVG with RGBA rgba #...0 #...F
    <svg width="200" height="50" viewBox="0 0 200 50">
      <defs>
        <linearGradient id="g2" x2="200" gradientUnits="userSpaceOnUse"  color-interpolation="sRGB">
          <stop offset="0" stop-color="#0F00"/>
          <stop offset="1" stop-color="#F0FF"/>
        </linearGradient>
      </defs>
      <rect fill="url(#g2)" width="100%" height="100%" />
    </svg>
    </div>
    <div>
    CSS with rgba #...0 #...F
    <p style="width: 200px; height: 50px; 
    background-image: linear-gradient(to right, #0F00, #F0FF)
    "></p>
    </div>
    
    <div>Fully opaque colours:</div>
    
    <div> SVG with RGB #... + stop-opacity 1 1
    <svg width="200" height="50" viewBox="0 0 200 50">
      <defs>
        <linearGradient id="g3" x2="200" gradientUnits="userSpaceOnUse">
          <stop offset="0" stop-color="#0F0" stop-opacity="1"/>
          <stop offset="1" stop-color="#F0F" stop-opacity="1"/>
        </linearGradient>
      </defs>
      <rect fill="url(#g3)" width="100%" height="100%" />
    </svg>
    </div>
    <div> SVG with RGBA rgba #...F #...F
    <svg width="200" height="50" viewBox="0 0 200 50">
      <defs>
        <linearGradient id="g4" x2="200" gradientUnits="userSpaceOnUse">
          <stop offset="0" stop-color="#0F0F"/>
          <stop offset="1" stop-color="#F0FF"/>
        </linearGradient>
      </defs>
      <rect fill="url(#g4)" width="100%" height="100%" />
    </svg>
    </div>
    <div>
    CSS with rgba #...F #...F
    <p style="width: 200px; height: 50px; 
    background-image: linear-gradient(to right, #0F0F, #F0FF)
    "></p>
    </div>
    
    
    
    <div>33% dim colours:</div>
    
    <div> SVG with RGB #... + stop-opacity .333 .333
    <svg width="200" height="50" viewBox="0 0 200 50">
      <defs>
        <linearGradient id="g5" x2="200" gradientUnits="userSpaceOnUse">
          <stop offset="0" stop-color="#0F0" stop-opacity=".33333333"/>
          <stop offset="1" stop-color="#F0F" stop-opacity=".33333333"/>
        </linearGradient>
      </defs>
      <rect fill="url(#g5)" width="100%" height="100%" />
    </svg>
    </div>
    <div> SVG with RGBA rgba #...5 #...5
    <svg width="200" height="50" viewBox="0 0 200 50">
      <defs>
        <linearGradient id="g6" x2="200" gradientUnits="userSpaceOnUse">
          <stop offset="0" stop-color="#0F05"/>
          <stop offset="1" stop-color="#F0F5"/>
        </linearGradient>
      </defs>
      <rect fill="url(#g6)" width="100%" height="100%" />
    </svg>
    </div>
    <div>
    CSS with rgba #...5 #...5
    <p style="width: 200px; height: 50px; 
    background-image: linear-gradient(to right, #0F05, #F0F5);
    "></p>
    </div>
    
    <div>33% to 66% colours:</div>
    
    <div> SVG with RGB #... + stop-opacity .333 .666
    <svg width="200" height="50" viewBox="0 0 200 50">
      <defs>
        <linearGradient id="g7" x2="200" gradientUnits="userSpaceOnUse">
          <stop offset="0" stop-color="#0F0" stop-opacity=".33333333"/>
          <stop offset="1" stop-color="#F0F" stop-opacity=".66666666"/>
        </linearGradient>
      </defs>
      <rect fill="url(#g7)" width="100%" height="100%" />
    </svg>
    </div>
    <div> SVG with RGBA rgba #...5 #...5
    <svg width="200" height="50" viewBox="0 0 200 50">
      <defs>
        <linearGradient id="g8" x2="200" gradientUnits="userSpaceOnUse">
          <stop offset="0" stop-color="#0F05"/>
          <stop offset="1" stop-color="#F0Fa"/>
        </linearGradient>
      </defs>
      <rect fill="url(#g8)" width="100%" height="100%" />
    </svg>
    </div>
    <div>
    CSS with rgba #...5 #...5
    <p style="width: 200px; height: 50px; 
    background-image: linear-gradient(to right, #0F05, #F0Fa);
    "></p>
    </div>
    
    
    <div>66% colours:</div>
    
    <div> SVG with RGB #... + stop-opacity .333 .666
    <svg width="200" height="50" viewBox="0 0 200 50">
      <defs>
        <linearGradient id="g9" x2="200" gradientUnits="userSpaceOnUse">
          <stop offset="0" stop-color="#0F0" stop-opacity=".66666666"/>
          <stop offset="1" stop-color="#F0F" stop-opacity=".66666666"/>
        </linearGradient>
      </defs>
      <rect fill="url(#g9)" width="100%" height="100%" />
    </svg>
    </div>
    <div> SVG with RGBA rgba #...5 #...5
    <svg width="200" height="50" viewBox="0 0 200 50">
      <defs>
        <linearGradient id="g10" x2="200" gradientUnits="userSpaceOnUse" color-interpolation="linearRGB">
          <stop offset="0" stop-color="#0F0a"/>
          <stop offset="1" stop-color="#F0Fa"/>
        </linearGradient>
      </defs>
      <rect fill="url(#g10)" width="100%" height="100%" />
    </svg>
    </div>
    <div>
    CSS with rgba #...5 #...5
    <p style="width: 200px; height: 50px; 
    background-image: linear-gradient(to right, #0F0a, #F0Fa);
    "></p>
    </div>
    
    <div>
    <p style="width: 200px; height: 50px; 
    background: RGB(85 85 85);
    "></p>
    <p style="width: 200px; height: 50px; 
    background: #555;
    "></p>
    
    </div>
    
    <!-- begin snippet: js hide: false console: true babel: false -->

    OP’s code iterated to the point where it should be identical, but …

    html {
     background-color: black;
     color: snow;
    }
    :link { color: aqua; }
    :visited { color: lime; }
    
    svg {
     outline: 2px #0FF6 solid; /* to see viewport boundary*/
     overflow: visible; /* to see things outside viewport */
     margin: 2em;
     transform:scale(2);
     transform-origin: center left;
    }
    rect {
     stroke-width: 2px !important; /* To see if it matches the viewport or not */
     stroke: black;
    }
    
    body { white-space: pre; }
    Original SVG (with added surrounding white stops)
    <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="Layer_2" data-name="Layer 2" width="107.097" height="26.024" viewBox="0 0 107.097 26.024">
      <defs>
        <style>
          .cls-1 {
            fill: url(#linear-gradient);
            stroke-width: 0px;
          }
        </style>
        <linearGradient id="linear-gradient" x1="795.61" y1="-6.181" x2="707.657" y2="35.941" gradientTransform="translate(-701.986 26.024) scale(1 -1)" gradientUnits="userSpaceOnUse">
          <stop offset="0" stop-color="#FFF" stop-opacity="1"/>
          <stop offset="0" stop-color="#f0ce45" stop-opacity="0"/>
          <stop offset="1" stop-color="#fd00ac"/>
          <stop offset="1" stop-color="#FFF" stop-opacity="1"/>
        </linearGradient>
      </defs>
      <g id="Layer_1-2" data-name="Layer 1">
        <rect class="cls-1" x="0" y="0" width="107.097" height="26.024" transform="translate(107.097 26.024) rotate(-180)"/>
      </g>
    </svg>
    <!-- y coords had to be lowered by 20px, IDK Y (no pun indended) -->
    Original with css moved to attributes, and effective gradient path visualised:
    <svg width="107.097" height="26.024" viewBox="0 0 107.097 26.024">
      <defs>
        <linearGradient id="linear-gradient2" x1="795.61" y1="-6.181" x2="707.657" y2="35.941" gradientTransform="translate(-701.986 26.024) scale(1 -1)" gradientUnits="userSpaceOnUse">
          <stop offset="0" stop-color="#FFF" stop-opacity="1"/>
          <stop offset="0" stop-color="#f0ce45" stop-opacity="0"/>
          <stop offset="1" stop-color="#fd00ac"/>
          <stop offset="1" stop-color="#FFF" stop-opacity="1"/>
        </linearGradient>
      </defs>
      <rect fill="url(#linear-gradient2)" x="0" y="0" width="107.097" height="26.024" transform="translate(107.097 26.024) rotate(-180)" stroke-width="4" stroke="black"/>
     <line x1="795.61" y1="-6.181" x2="707.657" y2="35.941" transform="translate(-701.986 26.024) scale(1 -1) translate(0 -20)" gradientUnits="userSpaceOnUse" stroke="lime"/>
    </svg>
    
    
    SVG un-flipizorized (transfroms recalculated and products applied directly)
    <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="107.097" height="26.024" viewBox="0 0 107.097 26.024" color-interpolation="sRGB">
      <defs>
        <linearGradient id="linear-gradient3" x1="5.671" y1="11.917" x2="93.624" y2="52.205" gradientUnits="userSpaceOnUse">
          <stop offset="0" stop-color="#FFF" stop-opacity="1"/>
          <stop offset="0" stop-color="#f0ce45" stop-opacity="0"/>
          <stop offset="1" stop-color="#fd00ac"/>
          <stop offset="1" stop-color="#FFF" stop-opacity="1"/>
        </linearGradient>
      </defs>
      <rect fill="url(#linear-gradient3)" x="0" y="0" width="107.097" height="26.024"></rect>
      <line x1="5.671" y1="11.917" x2="93.624" y2="52.205" stroke="lime"></line>
    </svg>
    
    
    Div with CSS gradient. R.I.P. Yellow from the transparent stop.
    <div style="
     outline: 2px #0FF6 solid;
     width: 107.097px; height: 26.024px;
     transform:scale(2);
     transform-origin: top left;
     margin: 2em;
     display: flex;
     place-items: center;
     justify-content: center;
     background-image: linear-gradient(114.4deg, #fff 0 8.5%, #f0ce4500 0, #fd00ac);
    ">
     <div style="border: 1px solid lime; width: 97.51919555664062px; height: 0; width; transform: translate(-4px, 20px) /* = lies */  rotate(24.4deg)"></div>
    </div>
    
    Div with CSS gradient. Yellow is back with help of second background with background-blend-mode. R.I.P. transparency
    <div style="
     outline: 2px #0FF6 solid;
     width: 107.097px; height: 26.024px;
     transform:scale(2);
     transform-origin: top left;
     margin: 2em;
     display: flex;
     place-items: center;
     justify-content: center;
     background-image:
        linear-gradient(114.4deg, #fff 0 8.5%, #000000 0, #ffffff00)
    ,   linear-gradient(114.4deg, #fff 0 8.5%, #f0ce45 0, #fd00ac)
    ;
     background-blend-mode: multiply, normal;
    ">
     <div style="border: 1px solid lime; width: 97.51919555664062px; height: 0; width; transform: translate(-4px, 20px) /* = lies */  rotate(24.4deg)"></div>
    </div>

    (So with background-blend-mode in the last try I got pretty close to the goal (on black background it is indistinguishable from SVG), but lost the transparency, what renders it quite useless.)

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search