28

I'm applying a basic linear-gradient like this:

background-image:  linear-gradient(to top, red, rgba(0,0,0,0));

this behaves as it's supposed to everywhere except in safari where the transparent is rendered as a blackish/greyish color:

here's chrome (how it is supposed to be):

enter image description here

and here's safari

enter image description here

I've tried prefixing it with -webkit-, changing the rgba to rgba(0,0,0,0.001) but it never goes to solid transparent. is this a bug? is there a way to fix this?

here's a fiddle https://jsfiddle.net/2Lrp3sv1/2/

valerio0999
  • 11,458
  • 7
  • 28
  • 56
  • 3
    wouldn't it be better to set rgba(255,0,0,0) ? (for all browsers?) – vals Jul 15 '16 at 09:16
  • @vals that renders a red color in my css. that's weird.. works on jsfiddle though. i've tried rgba(255,255,255,0) but still, it becomes from greyish to whiteish :) not exactly transparent – valerio0999 Jul 15 '16 at 14:21
  • First, decide which **solid** color you want in the middle point. Then, we can calculate wich would be the final color ... – vals Jul 16 '16 at 07:38

6 Answers6

51

No browser renders transparent as rgba(255, 255, 255, 0), that's completely wrong. transparent is always rgba(0,0,0,0), as defined in the CSS Color 3 specification. However, a few years ago we changed how color interpolation works in gradients and specified it should happen in a premultiplied RGBA space, exactly to fix this issue and make interpolation with transparent work as expected. Looks like other browsers have implemented this change, but Safari hasn't yet. If you want gradients with Safari to look the same, you need to use color stops that utilize the transparent version of the color you are interpolating to/from (which may sometimes require two color stops at the same position, if these colors are different), in this case linear-gradient(to top, red, rgba(255,0,0,0)). Or just wait for Safari to catch up! :)

Lea Verou
  • 23,618
  • 9
  • 46
  • 48
  • 2
    Perhaps "renders" is the wrong word. Anyway, there will be people checking this answer whose eyes cross when you mention checking the spec. Is there a less technical and more accessible way to say this? I would upvote that. – AJFarkas Jun 12 '19 at 19:20
  • 1
    I was ready to write that Safari actually does the correct color interpolation, because a linear transition from `255,0,0` to `0,0,0` is actually `red` to `black`, but then I read the specs and about **premultiplied sRGBA space** and it makes total sense now and the link with the specs has the *EXAMPLE 23* which demonstrates an incorrect transition in non-premultiplied space with red to transparent being incorrectly calculated as black! Very nice and informative answer, +1. – Christos Lytras Jun 28 '21 at 19:00
  • And to this day, this behavior remains. Safari definitely is the new IE, people should stop using it. – Parziphal Jul 26 '22 at 08:55
46

This has to do with the way browsers render transparent.

For most browsers,

transparent === rgba(255,255,255,0)

But Safari renders it as

transparent === rgba(0,0,0,0)

So if you have a gradient from transparent to white (or rgba(255,255,255,1)), for most browsers you're only changing the alpha from 0 to 1 along the gradient.

But for Safari, you're changing the alpha from 0 to 1 and the color from 255 to 0, so you get a gradient of greys.

This drove me crazy for a while.

AJFarkas
  • 2,929
  • 1
  • 17
  • 15
  • 14
    You don't know. That's an innovation from Apple. – Nirmal Sep 28 '17 at 06:35
  • 15
    This is as incorrect as it gets and I wish people checked the specs or at least MDN before perpetuating misinformation. No browser treats `transparent` as `rgba(255, 255, 255, 0)`, it's always `rgba(0,0,0,0)`, it's the way different browsers interpolate that produces the difference OP is asking about. For more details check out my answer. – Lea Verou Jun 11 '19 at 17:14
  • 2
    Well, I'm glad you're so excited about learning. May I suggest that there are better ways of engaging with me? I'm perfectly willing to accept that this is not a sound technical answer, and even a different colloquial one would be much better, but this described what I saw at the time very well, and lead to understanding the solution. I can definitely come up with a less correct answer if you'd like. – AJFarkas Jun 12 '19 at 19:16
  • 1
    2021 update: see Lea Verou's answer for clearer insight into what's actually going on – tenni Oct 15 '21 at 03:53
35

Based on what @AJFarkas wrote, just replace transparent with rgba(255, 255, 255, 0) and it works fine on Safari!

In my case it was a linear gradient from white to transparent to white:

background-image: linear-gradient(to right, #fff 10%, rgba(255, 255, 255, 0) 25%, rgba(255, 255, 255, 0) 75%, #fff 90%);
antoni
  • 5,001
  • 1
  • 35
  • 44
4

for my case looks like changing from

background-image: linear-gradient(rgba(243, 243, 251, 1), transparent);

to:

background: linear-gradient(rgba(243, 243, 251, 1), rgba(255, 255, 255, 0));

works perfectly on safari.

amdev
  • 6,703
  • 6
  • 42
  • 64
1

i am seeing this in Safari, too. For me, it is fading to a light red, even though none of the colors I use is anywhere near red.

I solved it using 'transparent' as the value. Once I stopped using rgba, it looked like expected.

mxe
  • 151
  • 8
1

I had a situation where the colours in the gradient were being dynamically applied by a CMS, so here's a Javascript fix for this issue in Safari that takes this into account.

Assuming you're using a pseudoclass to achieve this, you can use js to return the element, create a new style tag and insert it into the head of the document.

<script>
    var col = document.querySelector('.my_div').style.color;
    var style = document.createElement('style');

    // Replace RGB with RGBA (we need the opacity)
    col = col.replace(/rgb/i, "rgba");
    col = col.replace(/\)/i,', 0)');

    //Create our new styles based on the returned colours
    style.innerHTML =
      '.my_div::before {' 
    + 'background-image: linear-gradient(to left, currentColor 0%, '
    + col 
    + ' 15%);'
    +'}';

    var ref = document.querySelector('script');
    // Insert our new styles before the first script tag
    ref.parentNode.insertBefore(style, ref);
</script>

Here's the source of this great trick

JimRush
  • 37
  • 3