How to change PNG color in CSS?
September 7, 2010 8:55 AM

How can I change the color of a PNG (for example change a white object to a red object), while preserving the transparent background in HTML/CSS -- perhaps using some sort of an overlay?

For example, imagine a white circle smiley face with a transparent background; I want (via CSS) to change the eyes and mouth to a red smiley face with a transparent background.

Note that the important part is that the transparent background be preserved; otherwise it would be trivial to make the smiley transparent and set its background color.
posted by Doofus Magoo to Computers & Internet (19 answers total) 1 user marked this as a favorite
Conceivably there might be an IE-only way to do this using the non-standard filter/-ms-filter property, but otherwise CSS is not going to let you change colors within an image.
posted by enn at 9:02 AM on September 7, 2010


enn is correct, but you could make it work in other browsers using some HTML5 Canvas scripting.
posted by singularian at 9:04 AM on September 7, 2010


Create two images, one with the white smiley face and one with the red. Instead of an img tag use a div and set the background-image to the desired value.

<div id="foo"></div>

then in your stylesheet:

#foo { background: url(whitesmiley.png) no-repeat left top; }
#foo:hover { background: url(redsmiley.png) no-repeat left top; }
posted by Lokheed at 9:07 AM on September 7, 2010


Or, use jQuery to change the source attribute for the image based on whatever interaction you require. Again with two images, of course.
posted by Lokheed at 9:09 AM on September 7, 2010


Although I should add — if you want to create the effect of changing an image's color using CSS, for example on mouseover, there are a number of ways of using :hover to either replace which image is shown (e.g., to hide whitesmiley.png and show redsmiley.png) or, by using background images and background positioning, to show different sections of the same image (which is helpful because the whole image loads up-front so mouseover transitions are instantaneous).

On preview: Lokheed has the basic example of this; there are endless elaborations, but they will all require that you create both a white and a red smiley in an image-processing program beforehand; you won't be able to dynamically change the image to use any arbitrary color.
posted by enn at 9:09 AM on September 7, 2010


It's always going to be easiest and most reliable to make new images. But PaintbrushJS goes a long way to making this usable.
posted by artlung at 9:09 AM on September 7, 2010


Good responses so far, but to clarify a bit, this is going to be something where the user will be able to specify the color of their "smiley," so just creating the different versions of the smiley isn't an option.

And for what it's worth, I also know that these images could be created dynamically using PHP and GD, but that's very much a less-than-optimal solution if there's a way to do it using pure CSS.
posted by Doofus Magoo at 9:13 AM on September 7, 2010


That PaintbrushJS thing is pretty cool — thanks for the link, artlung.

Another altenative would be create the smiley as an SVG image in the page instead of a PNG; then you could use CSS to style the SVG elements (e.g. circle etc.) just like you can with normal HTML elements. Of course, the real-life use case you have might not be as easily rendered as a vector image as the smiley example.
posted by enn at 9:22 AM on September 7, 2010


Is it critical for them to be able to choose any color? Otherwise, you could have a palette for them to choose from and pre-generate smileys of all those colors using a script. The disk space involved would be inconsequential, and it would prevent load on the server from dynamically generating images.
posted by pengale at 9:23 AM on September 7, 2010


Of course, the real-life use case you have might not be as easily rendered as a vector image as the smiley example.

Yes, this. :)

Otherwise, you could have a palette for them to choose from and pre-generate smileys of all those colors using a script.

Good thought, but that's how we're currently doing it.
posted by Doofus Magoo at 9:28 AM on September 7, 2010


What are the browsers you are targeting?

You say "good thought, but that's how we're currently doing it."

Working solutions kick the ass of optimal solutions every time. Is this a case of "if it ain't broke, don't fix it?"

Will the unicode representation work for you? ☺
posted by artlung at 10:03 AM on September 7, 2010


You would want to create a transparent png with no color and render colored circles behind it, but I doubt there is any technique for that that would work across all browsers more easily than prerendered smileys and no userchoice.
posted by rhizome at 10:50 AM on September 7, 2010


Could you make a mask that overlays the background and removes the parts where you don't want the color with the (hopefully) color of the smiley and the background of the part where the smiley is placed?

In this example, I created a background where the user was able to use a color-switcher to specify html colors (b81818 in this case) to create a gradient background that faded from white to the color specified.

back_grad.png is a pic that starts white then goes to alpha clear.

body {
background:url("img/back_grad.png") repeat-x fixed center top #B81818;
}

posted by sleslie at 11:38 AM on September 7, 2010


Here's a way to do it in html and css using border-radius:
<style type="text/css">
div#wrapper {
	width: 80%;
	padding: 50px;
}
.smiley {
	height: 100px;
	width: 100px;
	position: relative;
	border-radius: 50px;
	-moz-border-radius: 50px;
	-webkit-border-radius: 50px;
}
.smiley .eye {
	height: 12px;
	width: 12px;
	border-radius: 6px;
	-moz-border-radius: 6px;
	-webkit-border-radius: 6px;
	position: absolute;
}
.left-eye {
	top: 20px;
	left: 30px;
}
.right-eye {
	top: 20px;
	right: 30px;
}
.mouth {
	-webkit-border-bottom-right-radius: 25px;
	-webkit-border-bottom-left-radius: 25px;
	-moz-border-radius-bottomright: 25px;
	-moz-border-radius-bottomleft: 25px;
	border-bottom-right-radius: 25px;
	border-bottom-left-radius: 25px;
	height: 25px;
	background-color: #000;
	position: absolute;
	width: 50px;
	left: 25px;
	top: 60px;
	
}
/*you can programmatically set the colors or even background images of these here */
div#wrapper, .smiley div {
	background-color: #c00;	
}
.smiley {
	background-color: #ccc;
}

</style>
<body>
<div id="wrapper">
	<div class="smiley">
		<div class="left-eye eye">&nbsp;</div>
		<div class="right-eye eye">&nbsp;</div>
		<div class="mouth">&nbsp;</div>
	</div>
</div>
</body>

posted by artlung at 12:45 PM on September 7, 2010


Make a face that's transparent except for the outline, eyes, and mouth in black. Now make everything outside the circular outline white.

Lay that on top of a colored background.

Change the background color using css.
posted by orthogonality at 1:50 PM on September 7, 2010


In Firefox you can apply SVG filters with CSS, but I don't think there's a pure CSS cross browser option.
posted by robertc at 2:36 PM on September 7, 2010


It's fairly simple:
  1. Make a face that's just the white pixels, with transparent background and eyes + mouth.
  2. Place a square div behind it, of correct size and position to show up behind the eye and mouth hole, but not outside the circle. If the geometry of the smiley face doesn't allow you to do this with just1 div, use 2, one for the eyes and for the mouth.
  3. Change the color of said div via css.

posted by signal at 3:52 PM on September 7, 2010


"If the geometry of the smiley face doesn't allow you to do this with just 1 div, use 2, one for the eyes and one for the mouth."
posted by signal at 3:55 PM on September 7, 2010


I made a tool to toy with colors - this is the CSS and border-radius way: http://lab.artlung.com/css3-smiley/
posted by artlung at 4:34 PM on September 7, 2010


« Older Accessible, affordable 2BR in Berkeley   |   The middle ground will come, I know it will, but... Newer »
This thread is closed to new comments.