Place text over images on hover without JavaScript

Thanks to CSS3 and better and better browser support for it, we can accomplish things now with CSS that previously required fancy JavaScript work. In this tutorial, I’ll walk you through fading in text over an image on hover.

Tutorialย Updated

I updated this tutorial to show you how to take advantage of even more CSS3 properties. The update is available here.

Here’s a sample of what we’ll be creating:

If you move your mouse over those images, you’ll see some text fade in that tells you where each photo was taken. When you move your mouse away, the text fades away.

Nice, right? And look ma, no JavaScript. So let’s get started.

Step 1: HTML Markup

To keep things neat and tidy, I’m going to put my images in a list. Each list item will contain an image andย a couple of nested <span>‘s to hold the text that should appear when my site visitors hover over the images. Then I’ll wrap all of that in a link.

I’d just repeat that list item and everything inside of it for each image that I wanted to include.

Step 2: Style the Images

Now that we’ve got the markup out of the way, let’s jump into the CSS. First up, we want to get rid of the bullets and line our images up across the page.

I’m setting the width and height of each list item to the width and height of my individual images, and then lining up the list items by setting them to display: inline-block.

Step 3: Place the text over the images

Now we’ll take that text description for each image and place it on top of the image.

There’s a few handy techniques here I’d like to draw attention to. First up, that rgba color I’m using for the background of the outer span. That will allow me to make my background color partially transparent by specifying an alpha value in addition to values for red, green, and blue. In this case I’m setting the background color to 50% transparent black.

Next up, I’ve set that inner <span> to display: table-cell. That lets me use the vertical-align attribute to place my text right in the middle of the image. If you’ve worked with CSS for any length of time, then you know that vertically-aligning anything to be in the middle of a container is pretty darn tough. This is a pretty straightforward solution that works without a lot of weird workarounds. It will work pretty nicely cross-browser, but be aware that it will not work in IE8 or any earlier version of IE.

ย Step 4: Show the text on hover

Now I only want to show that text when the mouse is over the image. We just have to make a couple of quick additions to the CSS file.

The change I made there was to set the opacity of the outer <span> to 0. That will make it completely transparent. Then when the mouse is hovered over the list item, I’ll change the opacity of the <span> to 1, making it fully opaque.

Step 5: Add the animation

It’s looking good so far, but we’re missing the animation. Well, you’ll be glad to know that’s it’s very simple to add that in. We just have to use the new CSS3 transition property.

Unfortunately, just now, we still have to include several lines that include the browser prefixes for the transition property, but in time that will shrink to just one line. We just specify which property we want to animate, opacity in this case, and then how long the animation should take.

There are some additional options we have, for example adding easing to the animation. If you’re interested in digging into CSS transitions a bit more, I recommend taking a look at Chris Coyier’s CSS Tricks article that explains transitions in detail.

This is also a great example of progressive enhancement – site visitors with browsers that have support for CSS3 will see the nice animation. But those who have older browsers that don’t support CSS3 transitions yet will still be able to see the plain hover effect, just as it appeared after Step 4, before we added the transition property.

And that wraps it up. That wasn’t so hard, was it? Have some fun experimenting with adding different CSS transitions – you can accomplish some really nice effects – like animating the color changes on menu items and links. Post a link here to what you’ve built so we can all see and learn from one another.

160 thoughts on “Place text over images on hover without JavaScript

  1. This is such a huge help! Thank you.

    How would I add one more line of text to be formatted differently? Like the font size?

    Thanks again!

    1. You’d just have to include it inside the container that’s holding the text and probably give a class to the div or paragraph that holds the text so that you could select that text to style it separately.

    1. The CSS I used will remove any bullets. You could use any markup you’d like for this – you’d just have to adjust the CSS to match the markup you’d rather use.

    1. Yes, though you’d have to be careful about the markup inside the table cells – absolute positioning does not work reliably cross browser inside of table cells.

      1. I know this is an older tutorial – but, I have images in a table and trying to figure out how to get this to work with images in a table – can you please advise? Not sure what I need to change inside the table cell to make this work. Thank you for your time.

    1. You can either float your images or switch their display to inline-block. If you look carefully at the CSS for step 2, you’ll see that I’ve used display: inline-block to display my images side-by-side.

      1. I’m having the same issue, my images are all under each other not next to each other. I copied your code exactly & they will not go next to each other.

  2. Thanks for very simple and easy tutorial. How can we make this work on iphone given that hover doesnt work on iphone.



    1. When you say ‘make this work for on iPhone’, what exactly do you mean? What do you consider to be ‘working’ on iPhone? That’s going to depend on your goals, your audience, etc.

      You might decide to just show the text in a different way for iPhone, or maybe not to show the text at all. There’s no one answer that’s going to work for all cases.

      1. Thanks for your reply. This question is purely technical not related to user experience. Since hover is replaced by touch in mobile, was wondering how to show the same effect (Showing some text when user selects/touch an image ). Both hover and click event will be triggered with a single touch.

        1. Since there is no such thing as hover on a touch device, you can’t replicate hover functionality. Most touch devices will trigger hover events on touch, but in this case, the images are also links, so as you’ve observed, you see the text, but the browser also follows the link. It would be possible to add some JavaScript for touch devices that would show the text on the first tap, and then follow the link on the second tap, but I’m not sure how valuable that would be – it might end up being more annoying than anything.

  3. It works great for a single image! But how to do it for a group of images? I’ve got a group of images which must contain the same class for some reasons. Then the code didn’t work, I tried to give each a different ID and replace the above code with the ID, but it still didn’t work.

    1. This code example is working on a group of images already – I have this working with three images in this example, but it would work just the same with one or twenty or one hundred images. I haven’t used any ID’s – I’ve used all classes.

  4. So this works great, except for the image hover shows up on the upper left hand side of the page. for every image. Why is this appearing so far away?

    1. It sounds like the container for the image is missing position: relative. Be sure to add that in, and then the position for the hovered text should be correct.

  5. I have a border-radius on my image, which does not show up with relative positioning. Is there anyway to fix this without javascript?

      1. Hi Natalie,

        I applied your code and it works fine. However, when i expand the width of the text-content bigger than than the image and i could not make the table stay on top of every images although I add the visibility: visible on the hover section. Please let me know what i did wrong.


  6. Awesome Tutorial, thanks alot Natalie!
    I have a further question, how do i get this effect working on an image slideshow loop? So, there are moving images from right to left via keyframe animation and i want your effect to show up when i’m hovering over a single image.

    Is this possible?

    Have a nice weekend!

    1. Yes, Sandra, that’s possible – you could apply this same technique to a slideshow. You’d just hide the captions, then show them on hover just as I’ve done here.

    1. What version of IE did you test? Not all versions of IE have support for CSS3 animations, and some older versions also do not support the :hover pseudoclass on elements other than links. You’d have to do some workarounds to get this working in those versions of IE.

      1. Thanks, looks great on my website sidebar using multiple vertical images. (Code worked fine once I removed the closing /ul tags between images, until the last one). Not working at all with Internet Explorer 8.0 though. At least not using IE default settings. The hover text is constant, does not appear or disappear with onmouseover/out. IE Compatibility view only makes things worse. But then, does any web page or content work properly when using IE? Lol not very often.

  7. This is the best tutorial! I have been looking at so many and none of them worked except for this one! Thank you very much!!!!! I am solo happy! ๐Ÿ˜€

  8. Hello Natalie and thanks for the great tutorial/s.
    I have just started learning a bit of web coding and I have had a go at your text over images code. At this stage I am just experimenting with one image on an otherwise blank “test page” that I use for development. I have posted the code at but I cannot work out why the image has a blue line at the bottom of it. If you have the time and inclination might you be able to tell me why the code is rendering this line.
    Thanks again Pedro.

    1. The blue line is happening because of a conflict between the styles that you’ve set for your navigation and the image rollover. You might want to apply those navigation styles only to your navigation rather than to all unordered lists and list items on the page.

  9. Thank you so much for this awesome tutorial! I’ve been searching for a “text over image” hover effect, which would work in Contenido without much trouble, for several days. This one just works excellent! Thank you, thank you, thank you! ๐Ÿ˜€

  10. Awesome tutorial. Quick question, how would I make the hover responsive? The image is now, responsive but the hover expands the full height? I tried over-flow:hidden,max-width/height:100%, no luck? Any tips? Thanks ๐Ÿ™‚

    1. Hi Julia! If you set the width of the image container to a percentage rather than a fixed value and left out the height declaration, you could then use top, left, bottom, and right values to absolutely position the text over it, making it automatically fill that same space.

      1. Thanks for the tutorial. I have the same problem. How can I make it responsive?

        what do you mean with “you could then use top, left, bottom, and right values to absolutely position the text over it”

        If I remove the code, the image is responsive, with de code, no.

        How can i make it responsive?

        can you copy some code?

      2. Hi! I have the same problem! Can you help us ? ๐Ÿ™‚ I have four images inline-block, width of each is 25% so they respond to change window size. I have no idea how to make the text always in center of image.

  11. Hi Natalie. Sorry to trouble you again but is there anyway that I can position these text on hover images precisely on a page using code like

    #content img {
    border: 1px solid #000000;
    border-radius: 8px;
    height: 225px;
    position: absolute;
    width: 300px;
    #img1 {
    left: 150px;
    top: 450px;
    Here is an image at

    1. I’m not sure what your question is. Can you be more clear about what you’re trying to do and what’s not working the way you expect in your example?

  12. Sorry about that poorly worded question it was a case of me trying to do too many things at once. The text on hover code is working flawlessly but I want to precisely position one or more images on my web page at Parola for I cannot work out how to position an image with you hover code attached, exactly where I want on a page. For example on this page the code that is positioning that image is

    <img width="183" height="213" style="position:absolute; top:730px; left:157px;"

    So how would I incorporate that code with the text on hover code?
    Likewise, further down that page there are another five images of various sizes and positions and I would like to have your text on hover code work on them as well. Thank you so much for taking the time to help me Natalie, I do appreciate it and I hope you have a happy and safe Easter. Pedro.

    1. This is more help than I’m able to offer in a comment thread. Perhaps try posting your sample code and question on Stack Overflow.

        1. I am still not sure what use this is because I think that once I try to incorporate it into any responsive type code it will break because of the positioning I have applied to the image and hover text, but as an academic exercise I found it helpful to aiding my learning . Thanks Natalie and I hope you do not mind me posting it here just in case anyone else might find it useful

  13. Thank you for this coding! In IE8 the text simply appears over the images. Is there a simple way to add a conditional line to turn off the text when a browser does not support the script? It doesn’t show the text on my android browser which is great because it pops up after you click, confirming your selection.

  14. Thank you so much.

    It worked!

    However, I am not able to get two images side by side. Perhaps I am using the float function wrong? Where exactly does the float come here?

    1. I didn’t use float for this example. Instead I used display: inline-block.

      It should work for float as well – just remember that you’ll have to clear your floats.

  15. Exactly what I’m looking for ๐Ÿ™‚

    However, the image I am using is not square, it’s a circle. Is there a way I can make the hover appear only on the circular image without corners?

  16. Hi Natalie

    Great article thanks. I’ve been trying to do this for ages and you’re the only one that’s explained it clearly enough for me to follow.

    Can you please help me further though, my home page consists of 9 images that are displayed in a 3×3 grid which I created using tables in WordPress. Each cell has the HTML section of your tutorial in it for each image.

    If you take a look at you’ll see, however the hover box is not aligned to the image and I can’t work out how to sort this, do you have any ideas please ? I think it’s got something to do with the bullet point pushing the image down. I would really appreciated your help.

    Thank you

  17. I am trying to do this but I have created a tile type layout of images (no tables). My overlay is always appearing at the top of the page. How can I change this?

  18. I wanted to do three things:

    1) Display lots of thumbnail images simply, using an unordered list
    2) Have them all act as links
    3) Have a short text description popup on hover

    Your page is the only one I found which explains how to do this, and in such a clear, concise manner. Thank you so much for publishing your technique!

  19. I generally flip through tutorials like the Sunday Sports section because most are written too complicated. This is not one of them.

    Very well written and easy to understand!

  20. I don’t have an url to click on. But I want to show the visitors only the text and that is working. But on a mobile when you have six pictures, when you click for example on the third, fourth, fifth and sixth image the text is still over the picture but the you see the top of the pages and you have to scroll to that image. How can I change this that the image where you click on is still in the ‘picture’ ๐Ÿ˜‰

  21. This was very helpful and clear, Thank You! My question: I’m not much of a coder, I guess you could call me a “google it” coder, which is what brought me here. Is there a way to set the css so that the image sizes can change throughout my website and the dimensions of the image and the overlay are determined by the html at each image? With my little understanding of this stuff, all I would know to do is create a new css class for every different size image on my website, but that seems like it’s not the only or best way. Thanks again!

  22. Hey,

    Great tutorial. I am trying to add this to my project using foundation 5 and having some issues. I tried with just the one class for the images (‘img-list’) but the hover content would show up only on the first image on mobile (the first and second showed up on the first) so I changed some things up. Here is my html:

    Hillary Barrack-PalmerFounder
    โ€œOur goal is always the same;

    Bill PalmerFounder

    Nancy NewmanDirector of Account Management


    Alex Miller

    Our extended Ohana

    and my css:

    span.hillary-content {
    background: rgba(0, 0, 0, 0.5);
    left: 0;
    color: white;
    margin-left: 11.8em;
    cursor: pointer;
    display: table;
    height: 175px;
    position: absolute;
    top: 0;
    width: 175px;
    border-radius: 100%;
    opacity: 0;
    -webkit-transition: opacity 500ms;
    -moz-transition: opacity 500ms;
    -o-transition: opacity 500ms;
    transition: opacity 500ms;
    .team li:hover span.hillary-content {
    opacity: 1;
    span.hillary-content span {
    border-radius: 100%;
    display: table-cell;
    text-align: center;
    vertical-align: middle;

    So how bad did I break your technique? ๐Ÿ™‚

  23. my last comment did not have my html. Here it is, in all it’s glory:

    Hillary Barrack-PalmerFounder
    โ€œOur goal is always the same;

    Bill PalmerFounder

    Nancy NewmanDirector of Account Management


    Alex Miller

    Our extended Ohana

  24. Thank you for your awesome tutorial! It did exactly what I was looking for. I love CSS3 and would rather find a Css3 solution before using JavaScript.
    On my website, I created menu bars using only images on . Two of three have descriptions in the form of unordered lists. I wanted a display that would only appear in a hover state and would look a lot nicer than a title element for my third menu bar images. I added your coding to my menu bar that is already activated by JavaScript to โ€œsproingโ€ or expand and enlarge in a hover state (another tutorial). With a little tweaking I managed to bring the two together and complete what I had envisioned.
    I have added your tutorial to my link page along with the rest of the resources I used that went into building my website. Thanks again!
    Mike Lasher

  25. Hi Natalie,

    Many thanks for the brilliant tutorial. I am struggling with the opposite problem to some previous comments; I’d like to arrange my photos vertically down the RHS of my webpage. Is it as simple as deleting the display: inline-block; line of code?

    Many thanks,

  26. Hello, Thank you for the great tutorial!
    I’d like to have text appear in another div underneath the picture when I roll over it.
    Can you help me with this?
    I suspect my CSS is off:

    opacity: 0;
    #pic1:hover .description1 {
    opacity: 1;

  27. Thank you for your tutorial. I have gotten it to work but I am trying to work out a few kinks.

    I am using 75px x 75px images. When I hover the caption does not fit the image and is cut off. I thought it would be a simple fix by adjusting the font size. But when the font size is adjusted it actually shrinks the entire hover content. So my text is smaller but the black background that fades in with it is also smaller.

    Do you have any suggestions on how to make the text smaller in the caption upon a hover?

  28. Thank you so very much for this. ๐Ÿ™‚
    This is exactly what I was looking for. I’m gonna use it on my site the soonest possible. ๐Ÿ™‚ ๐Ÿ™‚ ๐Ÿ™‚

  29. This is exactly what I was looking for – thank you so much! Written in such a clear, concise, easy to follow way too.

    (Also, major props for having a larger font size, my eyes thank you profusely ๐Ÿ˜‰ )

  30. Hi Natalie, thanks for the tutorial. In using opacity: 0 to hide the rollover text at first, you are in effect hiding text on the page. I bring this up because though unintentional, hiding text is against Google’s webmaster guidelines. If being found in search is something to consider, keep this in mind.

  31. Thank you for the great article! Iโ€™m trying to have the text so itโ€™s not hidden at the start – perhaps it could be a different colour (Might help with the google โ€˜issueโ€™ as above?). I have played around with opacity settings to no effect. Any pointers?

  32. Hi Natalie!

    First, thank you for the great and easy to read tutorial. My problem though, is this:
    my images have different heights (but the same width of 475px). So I was wondering if there is any way to tweak the code to make the same hover effect, but for the different heights (instead of the 150px you used for your images)?

    Thanks alot in advance!

  33. This is killer Natalie, thank you.

    For folks w/ using dropping this into a responsive framework like Bootstrap or Foundation (Ysabel, maybe in your case also) it seems that styling the span.text-content to have a width of 100% and a height set in rem works…

  34. Hey plz drop me a mail. On hovering over image the text content is showing somewhere on top left. I will send you the code. ๐Ÿ˜ฎ ๐Ÿ™

  35. Hello,
    Thanks for the great tutorial! I am just beginning to learn coding, and this might be a simple question, but I need the images to be 300px instead of 150 px, however when I change them in this section
    ul.img-list li {
    display: inline-block;
    height: 150px;
    margin: 0 1em 1em 0;
    position: relative;
    width: 150px;
    it changes the hovertext position but the image size remains 150 px. any suggestions?

    1. You need to change the images themselves not just the list items.
      Try this:
      ul.img-list img {
      height: 300px;
      width: 300px;

  36. Hi Natalie,
    This is just what I’ve been looking for.
    For some reason, however, my overlay is slightly above the images.
    I have the position:relative exactly as you do. I have the image width and height in both CSS and html set the same.

    Thank you.

    1. Figured it out, it was my image that was causing the problem.
      Again, thank you for posting this, just what I was looking for.

  37. Hi.
    Thanks for the great tutorial.
    The layer works great in pc, but in mobile, the layer don’t fit.
    Have any tips for me?

  38. I am having a problem with my absolute-positioned text (list items) not scaling with the image on resize. Can you please recommend a fix for this?

    Thank you!

  39. Hi there!

    First of all thank you for posting this! It’s really nice of you!

    I just have a question regarding the images. Instead of squares I used a portrait format. my images are 300 by 424, but they look so pixelated. I have them in high res, and the process is simple. you just make a 300 by 424 canvas drop your image and scale it down. AT 100% it looks great on psd but somehow when I put them in the grid it totally looses the sharpness. Any idea why?

  40. Thank you for the helpful code. And I am also impressed by all nice replies you have given to peoples question regarding this matter.


  41. Excellent tutorial,
    When used in a table the image goes completely out of place within the , what part am I needing to change so it stays aligned like the ones I havnt put the hover on?

  42. BRILLIANT! I read the instructions and links before trying and it worked like a charm the first time! You’re a goddess!

  43. This helped me a lot, but I am getting crazy about making the whole thing responsive to the size of the screen, maybe you are able to help me with this problem?

    Kind regards

  44. Hi,

    Great tutorial, thank you. Only trouble I’m having is that the images are vert aligned, rather than horiz. I can’t figure out what I’ve missed/done wrong. Any ideas please?

    ul.img-list1 {
    list-style-type: none;
    margin: auto;
    text-align: center;

    ul.img-list1 li {
    display: inline-block;
    height: 150px;
    margin: 0 1em 1em 0;
    position: relative;
    width: 150px;

    span.text-content1 {
    background: rgba(0,0,0,0.5);
    color: white;
    cursor: pointer;
    display: table;
    height: 150px;
    left: 10;
    position: absolute;
    top: 0;
    width: 130px;
    opacity: 0;
    -webkit-transition: opacity 500ms;
    -moz-transition: opacity 500ms;
    -o-transition: opacity 500ms;
    transition: opacity 500ms;

    ul.img-list1 li:hover span.text-content1 {
    opacity: 1;

    span.text-content1 span {
    display: table-cell;
    text-align: center;
    vertical-align: middle;


    Whitelines Snowboard Magazine

    Triathlete Europe Magazine

    Other Magazine Work

    Manydown Farm Shop

  45. MERCI !!!!!!!!!!!!!!!! I’ve just spent days trying to do that before I read what you explained so clearely ๐Ÿ™‚ thanks a lot!

  46. Thanks for writing up this walkthrough! This works great for web developer portfolios where you show a screenshot of the project and then some detail on hover. Thanks again!

  47. This was great! Very straightforward and easy to understand. Added a nice tough to my final school project. Have you done a tutorial on the clearbox widget or lightbox? Do you have youtube tutorials as well? You could give some of these guys on youtube a run for their money.

Leave a Reply