Reputation: 9087
I have a link. I'm using :before
to put an icon in front of it. Initially, the text is invisible (opacity: 0
) while the icon is visible (opacity: 1
). Upon hovering, the text will become visible. I'm using opacity as opposed to visibility or display because I need it to use the transition property.
I've found that I can do this in Chrome by setting the :before
element to display:block
(without this, it refuses to have an opacity different from the rest of the link). Unfortunately, I've also found that even after adding this, Firefox won't let it have an opacity different from the rest of the link.
The page in which I'm using this effect can be viewed here (and here's the codepen source), but I've boiled the problem down to its simplest form and reproduced it below.
a {
opacity: 0.5;
}
a:before {
display: block;
float: left;
opacity: 1;
content: "+";
}
<a href="#">Sample Text</a>
As you can see, in Chrome (I'm using v37.0 x64 for RPM Linux), the plus sign is fully opaque, while the text is partially transparent. This is the intended behavior. However, in Firefox (I'm using v31.0 x64 for RPM Linux), the entire thing is partially transparent.
I should mention that there are some stipulations to my project which limit my options here. Firstly, I cannot change the HTML, as it is all being generated from absurdly generic markdown. Secondly, I am using no images. On the upside, however, I have no need to support older browsers.
Which brower's implementation is "correct"? Is there a better way to achieve the effect I've described? Barring a more robust solution, is there a workaround?
Upvotes: 2
Views: 418
Reputation: 13546
I believe that the behavior of Firefox is correct. Pseudo elements are not DOM descendants of the element, but they belong to its render tree, so the opacity of the element should apply to them, too. Although opacity
isn't an inherited property in the terms of CSS inheritance, it applies to the element as a whole, with everything inside it that does render at all. Generated content shouldn't be exceptional, it's just a part of element's rendered content.
If elements with opacity are nested, their opacity values multiply (e.g. placing and element with opacity:0.5
inside the container with opacity:0.5
and transparent background will have the same visual effect as giving it opacity:0.25
). So setting opacity:1
to the nested element means not "restore the opacity to fully opaque", but "preserve the opacity of the container". Since opacity
isn't inherited property in terms of CSS inheritance, contents of the element with opacity have opacity:1
by default.
IIRC, very early experimental Mozilla implementation (pre-Firefox 0.9) allowed to 'undo' the opacity of the nested elements by specifying values more than 1, e.g. -moz-opacity:2
, but the spec never allowed this.
The behavior of Chromium looks like a bug because it happens only for display:block
(or float
, which also results in block display, but surprisingly not with position: absolute
) and only for opacity: 1
(which is default, so omitting it has the same effect). Changing the opacity to 0.99
or adding -webkit-backface-visibility: hidden
(popular hack that creates a 'render layer' for the element, often fixing some render bug and enabling GPU acceleration) results in the same rendering as in Firefox, which is expected result according to the spec (as far as I understand).
Upvotes: 1
Reputation: 15915
:before
pseudo element is actually rendered inside the element you are applying it to since it's intended as content before the content in your element.
opacity
also get's inherited on all child elements so you will have to change your markup to actually have the element outside your div.
Read about transparency specs on http://www.w3.org/TR/css3-color/#transparency
Or you could use rgba()
to set the color of your text - ofcourse this approach works only if you are manipulating background or text color, but at least is consistent cross browsers.
The behaviour you are experiencing in Firefox could be a Firefox bug since the other browsers render the same as chrome (tested chrome, opera and internet explorer with same results).
a {
color:rgba(0,0,0,0.5);
font-size:60px;
font-weight:bold
}
a:before {
display: block;
float: left;
opacity: 1;
content: "+";
color:rgba(0,0,0,1);
}
<a href="#">Sample Text</a>
Upvotes: 5