Grow your CSS skills. Land your dream job.

Replace the Image in an <img> with CSS

Published by Guest Author

The following is a guest post by Marcel Shields. Marcel in a difficult place where he needed to change the image on a page but didn't have access to the HTML. Rare, but I'm sure we've all be in weird situations like this. He explains how he managed to replace that image without needing to access the HTML or JavaScript. Also turns out to be a pretty good way to replace anything with an image.

I just wanted to share something I found really cool about using CSS box-sizing. Chris wrote a really good introduction a few years back if you're not familiar with the property. It makes things a lot more sane when thinking about layout. People love it so much, they put it on everything like hot sauce. I wanted to share how I found it useful as (yet) another image replacement technique.

A few days ago at work, I was asked to replace an <img> on our site with another image hosted elsewhere. Simple right? But the catch was I would not be able to replace the markup as it was already deployed to production, but could inject CSS or JS through our CMS. For whichever technology I chose, it would be inserted on all site pages. I only needed on one specific page, and the attributes of parent containers were non-specific to the desired page.

  <title>Really Cool Page</title>
  <!-- .header would be across site on other pages with different children, so no background image adding -->
  <div class="header">
    <img class="banner" src="">

This is simple to do with JavaScript, but I wanted to see if there was another, even simpler, way. After a few iterations in Chrome Dev Tools, I thought to use the box-sizing property to keep dimensions strict, add the new image as a background image, and just push the inline image out of the way with padding and see what happened.

/* All in one selector */
.banner {
  display: block;
  -moz-box-sizing: border-box;
  box-sizing: border-box;
  background: url( no-repeat;
  width: 180px; /* Width of new image */
  height: 236px; /* Height of new image */
  padding-left: 180px; /* Equal to width of new image */

It worked beautifully. Here's what's cool:

  • It works on just about any element, even empty ones like <img> or <hr>
  • Browser support is excellent (Chrome, Firefox, Opera, Safari, IE8+)
  • Refrains from using SEO unfriendly display: none or other properties

That last point seemed important, as it works really well for text replacement too without any adjustment. Check it out!

Check out this Pen!


  1. Nice find as far as the tag is concerned.

    As far as text replacement’s concerned, wouldn’t the same thing work without box-sizing if the height was set to 0px and padding-top set to 250px with overflow hidden ?

    • Marcel Shields

      Yes, as far as I’ve seen 0px height will work too for the text replacement, I was only pointing out a double use. Especially if you follow the *{box-sizing:border-box} movement, you only need to then add the dimensions, bg image, and padding to the selector. Thanks for your comment.

    • Truyen
      Permalink to comment#

      Work for me and very clean

  2. If only you could use :before / :after on img elements :P

    Any reason for box-sizing: border-box instead of width: 0? (Gareth sort of beat me to that)

    • Ben

      In your picture it looks like you’re strangling that aligator…

    • I might have been a little bit scared to lose a finger, but I promise you no animals were harmed! ;)

    • Marcel Shields

      No real reason against it that I can think of. Honestly, I just found this way to be a sort of multi-use option. And yeah, pseudo elements were attempted with the unfortunate results.

  3. Ramesh Chowdarapally

    simple and good chris.

  4. You can simply use img { content: url(); } to replace an image in the <img> tag:

    No need for any other background or size styles.

    • Even in Firefox 21 that doesn’t work, but I wish it did!

    • MaxArt

      That’s cool! Worked like a charm in Chrome.
      But if Chris is right, browser’s support is still quite bad ATM.

    • Still no Firefox support? Bummer, I should have checked that first.

    • Firefox 21 still doesn’t have proper support for flexbox, like the v20. It only is supported in Nightly v23 build, maybe they’ll fix this issue too.

    • gcyrillus


      I always thought that was a mistake it worked in some browser. CSS content , to my point of view, shouldn’t be used to replace inline content , it is a tricky lie, why won’t we have a clear rule like : replace instead.
      Just my point of view :)

    • Dmitry
      Permalink to comment#

      Not surprising that this doesn’t work in Firefox – it’s against the spec.

    • John
      Permalink to comment#

      that works on chrome ,not firefox/IE

  5. For images, it looks like an awesome solution! The only thing I noticed with the text substitution is that the old text remains selectable. Not a huge problem, but in some cases it might be.

  6. I think there is no need for “box-sizing”. I wrote something about background images inside img when “modern browsers” meant IE6 ;)

    See this pen: width or height = 0 + padding does the trick

  7. Think this could be used for image replacement at specific breakpoints?

  8. Man, quite a clever way of replacing an image, thanks for the insight :)

    For text replacement I usually use:

    .hide-text {
      text-indent: 1000%; /*I use 1000% since Firefox shows a bit of the text*/
      white-space: nowrap;
      overflow: hidden;

    Now, in the spirit of improving this useful article, your argument that display:none; isn’t SEO friendly, is not true.

    Using display:none; has no negative SEO impact whatsoever, it probably never will, at least for many, many years from now.

    display:none; is used for too many things to be consider a “Black Hat” SEO technique: It’s used in all kinds of widgets, tabs, accordions, sliders, slideshow, carrousels, lightboxes, and of course, image/text replacement techniques.

    Check this forum out at

    And here’s another forum in

    It’d be nice if you edit your 3rd bullet item in the article so as to not mislead readers :)

    Thanks again!

    • Marcel Shields

      Thank you Ricardo for the complement!

      As far as the SEO stuff goes, the StackExhange link quotes Google saying “Merely using display:none will not automatically trigger a penalty. The key is whether or not there is a mechanism – either automatic or one that is invoked by the user – to make the content visible”. There’s no mechanism available here, so it doesn’t quite dispute what I am saying about using it in this specific instance. But I could agree otherwise and would be more than willing to say it is “possibly SEO unfriendly” instead. How’s that?

    • Hey Marcel,

      Thanks for your reply.

      Well, the thing is that display:none; has absolutely no negative SEO impact at all, so assuming that “it’s possible” that it does it’s an incorrect assumption/statement altogether.

      I’m trying the keep our conversation within Chris’ post subject as much as possible, so I won’t go the “he said – she said” route :p, but the only reason I referenced Stackexchange forum was only to show what’s been said about this subject for so long without any actual proof of negative SEO impact in any given way.

      The way I see this subject is quite simple: It’s our own prejudice of the concept of having something “hidden” that creates that fear of being penalized by Google (or any search engine for that matter) when using display:none; to “hide” something, when in reality all we’re doing is “delaying” the display or, as in your exercise, replacing one object for another.

      If at least you could reference our discussion in the article so people can decide on their own if using display:none; is right for them or not…

      Just sayin’ :)

      Thanks again!

  9. GreLI

    Well, Roman Komarov aka kizu developed <img> replacement couple years ago.

    See the answer for ‘does text-indent also indents <img>?

  10. Cool! :)

    But, to replace text with an image I would use text-indext:

    .text-replacement {
      display: block;
      height: 250px;
      width: 250px;
      text-indent: -100%;
      overflow: hidden;
      background: #333 url('url-string') no-repeat;
  11. Make sure the content you’re replacing is not something that really needs to be removed, not just hidden. maybe that is obvious, but double-clicking on the Shop Talk logo in the Code Pen above selects the padded-away text, Chrome offers to define the term, the placeholder text can be Command-C copied onto the clipboard, etc. This probably doesn’t even merit mention with this crew, but it could bite you if there is a legal or regulatory reason for replacing a text element’s content with an image and then the css is turned off.

  12. It might be interesting to make a code snippet or WordPress plugin that handles retina images this way. You could spit out a bit of CSS in a STYLE tag right after the image that replaces it with a higher resolution image. The CSS just needs to be wrapped in a media query. I’ve been using Javascript-based solutions to do this, but it seems like this would be better.

    Anyone have thoughts?

    • I really like this idea. Without too much thought or experimenting I think it should work great.

  13. Cool technique!

    And scary one. Why scary? Well, let me explain. Just 15 minutes ago I was reading an article about site security and now I’m reading this one. I suppose you’ve probably guested the way these two things chained in my head. Just imagine, some evil person wants to highjack some traffic from your partner’s banners. The only thing he needs is to find some vulnerability to inject his javascript in your site. With this evil script he can replace links on the banners with the links he needs. And with the same script he can inject inline styles from above to replace initial banner image with some fancy new one, probably more appropriate, to make it all look a little bit less suspicious.

    Can you imagine css-tricks or smashingmagazine trying to sell you kittens or fridges with their ads? Yeah, sounds ridiculous!

    P.S. If you spot Chris trying to sell you kittens, ring security alarm bells!

    Just sayin’ :)

  14. Hi Marcel, First of all i want to thanks you to for this article. And there are lot of discussion and argues on this great topic. So this is good. And I am totally agreed with “Guille Paz” it shows an example that is really good and nice implementable.

    But, to replace text with an image I would use text-indext:

    .text-replacement {
    display: block;
    height: 250px;
    width: 250px;
    text-indent: -100%;
    overflow: hidden;
    background: #333 url(‘url-string’) no-repeat;

  15. Patrick

    @ Kyle, Ricardo, Marcel: What if you use a 1px transparent image as default and use CSS to load the appropriate image? Then both images being downloaded isn’t really a big problem, right?

    • There are three issues with that approach:

      There’s no regard for the user, the content and the user experience. A 1px transparent GIF is not relevant content, thus is no use… to the user :p, hence, it impacts the user experience, because:
      You’re still dealing with 2 HTTP requests, and in this case is totally unnecessary to have them because the use of a 1px transparent image is an unnecessary visual/layout hack that’s going to delay page/site performance. And regardless that the 1px transparent image only weights a few bytes, it’s the HTTP request that is recommended to avoid in the first place.

      Check One Less JPG for a clear example of how removing images (or loading them conditionally with Moderinzr) is a good idea for site performance.

      Not SEO friendly. I’m not saying it has negative SEO impact. I’m saying that is not SEO friendly because images help compliment the content, and if this 1px transparent image with a descriptive alt and title tags is only a 1px transparent image, it really doesn’t help enhance the content for SEO.

      Not sure when one would deliberately use a 1px image to replace it with another image though :p

  16. nicole

    Thanks for this tip, I used it already.

  17. William Andrus

    Interesting note: The source of the image is still pointing to the old, when I right click and view image properties.

  18. gcyrillus

    Without border-box, it’s a way to show & switch sides both images within only one img tag, or slides transition hovering it , etc … ;)

  19. I was playing around with this a couple months ago, to use an image as its own container for responsive aspect ratio switching. Using the image’s src as its css background could be useful in production (otherwise the two image download makes this a hack for when you need it).

    It would be really useful if there was a css way to use an image attribute as a css value, but right now you need to enter it manually in the css or use javascript.

  20. Interesting idea. It a cake walk with js, but doing it with css is commendable. :)

  21. Binyamin

    Searching engines like Google Search not just indexes pages, but also renders the page visual appearance with Chromium. So any “trick” that replaces text or image from its appearance and does not suits to “real” page context will be sooner or later banned by featured Searching engines.

  22. Think this could be used for image replacement at specific breakpoints?

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".