18

I have a responsive slideshow-type layout with captions below each image.

I'm attempting to get the caption to be the same width as the image. The problem is that the image is scaled to fit in the browser vertically, and my captions are the getting the width of the image prior to scaling.

Fiddle

#big_container {
  display:block;
 position:relative;
 width:100%;
 padding-bottom:40%;
 white-space:nowrap;
 overflow-x:scroll;
 overflow-y:hidden;
}
    
#big_container>div {
 position:absolute;
 top:0;
 right:0;
 bottom:0;
 left:0;
}

.little_container {
 display:inline-block;
 height:100%;
 width:100%;
 text-align:center;
}

#big_container figure {
  display:inline-block;
 height:100%;
 margin:0;
}

figure img {
 max-height:calc(100% - 40px); /* subtract height of caption */
}

figcaption {
 display:block;
 width:100%;
 text-align:left;
 box-sizing:border-box;
 margin:0;
 padding:10px;
 line-height:20px;
 background-color:#ddd;
}
<div id="big_container">
  <div>

  <div class="little_container">
      <figure>
        <img src="http://placekitten.com/500/440">
        <figcaption>
          have a kitty!!1
        </figcaption>
      </figure>
  </div>
  
  <div class="little_container">
      <figure>
        <img src="http://placekitten.com/450/400">
        <figcaption>
          moar kitty!
        </figcaption>
      </figure>
  </div>
  
  <div class="little_container">
      <figure>
        <img src="http://placekitten.com/300/440">
        <figcaption>
          too many kitty..
        </figcaption>
      </figure>
  </div>
  
  </div>
</div>

How can I make a caption which is scaled based on the width of a fluid image?
I'm hoping for a pure-css solution.

Update
It turns out my above attempt partially works in chrome and opera, but exhibits some odd behavior. I haven't found any bug reports on the subject, but I can't help wondering if this might be considered a bug in the browser.


For clarity, here's a brief outline of my exact requirements:

  • Caption element must be the same width as the image (it'd be nice to be able to left or right align the caption text to the edge of the image)
  • Image must not be cropped or stretched
  • Image and caption must both fit in their container (which may be fluid), using as much room as possible.
  • Above rules should hold true for images of any dimension
  • CSS only (compatibility with old browsers not a major concern, but it is a plus)

The html markup can be changed.

Community
  • 1
  • 1
gandalf3
  • 1,636
  • 4
  • 24
  • 40
  • Where is the code that changes from one image to another? – Sumner Evans Jan 30 '16 at 04:03
  • 1
    @jsve I intend to add some javascript to handle that later. The reason I'm hoping to do the layout in pure css is so that when javascript is disabled, all the images are still viewable in a similar manner to when javascript is working. – gandalf3 Jan 30 '16 at 04:06
  • it looks like the captions are actually just as long as the caption text forces them to be (not related to the size of the image before scaling). the parent element `
    ` is set to `display:inline-block;` so it's expanding to the size of the child element that's widest...which is the caption when the image is really small. If you change the caption to a single character, you'll see what I mean, I think. https://jsfiddle.net/0xmtq77z/1/ What do you want to happen in the situation where the text is wider than the image? are you wanting the text to wrap, or be trimmed, or...?
    – ryantdecker Jan 30 '16 at 04:08
  • @ryantdecker The [caption still seems larger than the image for me](http://i.stack.imgur.com/8zShB.png)..? (firefox) As for text, trimming is fine. I might set a min-width on `.little_container`, allowing the caption to be larger if the image is super skinny, but for my use it's acceptable to assume the caption will never be longer than the image. – gandalf3 Jan 30 '16 at 04:16
  • hmmm - sorry, I was only looking in chrome - seems the behavior is not consistent between the two. – ryantdecker Jan 30 '16 at 13:32
  • @ryantdecker Huh.. I tested this in opera too, and there it works exactly how I want — except when the browser window is resized, the image is stretched. Refreshing the page at the new browser size then fixes the stretching. Same thing in chromium. I wonder if the spec says anything about how this is supposed to work – gandalf3 Jan 31 '16 at 01:51
  • I poked around the google last night looking for something about this being a bug in FF...didn't spend a lot of time, but I didn't find much of anything. I wonder if it's related to the figure and figcaption elements somehow, but that's not really based on anything. interesting for sure... – ryantdecker Jan 31 '16 at 03:55
  • Are you going for something like this? https://jsfiddle.net/brettdewoody/96jxj5cq/ – Brett DeWoody Feb 02 '16 at 08:57
  • @gandalf3 I follow all updates of this question as I have a personal interest in an equal solution for a client of mine. With the exact requirement you set, it can't be solved without script. If you like, I can explain in a chat. So there need to be a compromise here, which I guess should be based on if image is more important or text, where one way could be to scale text on top of the bottom of the image, with a semi-transparent back color. I updated my answer with a 2:nd [fiddle](https://jsfiddle.net/LGSon/akfhh789/4/) / [snippet](http://stackoverflow.com/a/35141056/2827823) to show that. – Asons Feb 08 '16 at 13:40

5 Answers5

5

Updated

Based on the exact requirements you set for this question, it can't be solved with CSS only.

This is the best I was able to come up with.

Fiddle demo 1 (fixed height for text, image fully visible)
Fiddle demo 2 (semitransparent scaleable text on top of image with animation)

The trick I mainly used is having a hidden img to make up for the space and then a background-image to scale to maximum width/height with kept ratio.

I added the inline style background-image for convenience, so content can be handled within the html.

To make it perfect, a script is needed, which calculate the caption's content and adjust the image's/caption's reduction/height.

Snippet demo 1

html, body {
  margin: 0;
  white-space: nowrap;
  overflow-y: hidden;
}
.container {
  display: inline-block;
  white-space: normal;
  width: 100%;
}
.wrap {
  margin: 0 auto;
  display: table;
}
.image {
  display: table-cell;
  background-position: center bottom;
  background-repeat: no-repeat;
  background-size: contain; 
}
.image img {
  visibility: hidden;
  max-width: 100vw;
  min-width: 100%;
  height: calc(100vh - 80px);
}
.caption {
  display: table-caption;
  caption-side: bottom;
  height: 40px;
  line-height: 22px;
  padding: 8px;
  background-color: #ddd;
  overflow: hidden;
}
.right {
  text-align: right; 
}
<div class="container">
  <div class="wrap">
    <div class="image" style="background-image: url('http://placekitten.com/450/300')">
      <img src="http://placekitten.com/450/300">
    </div>
    <div class="caption right">
      moar kitty!
      moar kitty!
      moar kitty!
    </div>
  </div>
</div>

<div class="container">
  <div class="wrap">
    <div class="image" style="background-image: url('http://placekitten.com/500/440')">
      <img src="http://placekitten.com/500/440">
    </div>
    <div class="caption">
      have a kitty!!1
      have a kitty!!1
      have a kitty!!1
      have a kitty!!1
      have a kitty!!1
      have a kitty!!1
      have a kitty!!1
    </div>
  </div>
</div>

<div class="container">
  <div class="wrap">
    <div class="image" style="background-image: url('http://placekitten.com/300/440')">
      <img src="http://placekitten.com/300/440">
    </div>
    <div class="caption">
      too many kitty..
      too many kitty..
      too many kitty..
    </div>
  </div>
</div>

Snippet demo 2

html, body {
  margin: 0;
  white-space: nowrap;
  overflow-y: hidden;
}
.container {
  position: absolute;
  height: 100%;
  display: inline-block;
  white-space: normal;
  width: 100%;
  background: white;
  opacity: 0;
}
.wrap {
  margin: 0 auto;
  display: table;
}
.image {
  display: table-cell;
  background-position: center bottom;
  background-repeat: no-repeat;
  background-size: contain; 
}
.image img {
  visibility: hidden;
  max-width: 100vw;
  min-width: 100%;
  height: 100vh;
}

.caption-wrap {
  display: table-caption;
  caption-side: bottom;
  position: relative;
}
.caption {
  position: absolute;  
  left: 0;
  right: 0;
  bottom: 100%;
  height: auto;
  line-height: 22px;
  padding: 8px;
  background-color: rgba(0,0,0,0.6);
  color: white;
}
.right {
  text-align: right; 
}
.center {
  text-align: center; 
}

.container:nth-child(3) {
  animation: xfade 12s 0s infinite;
}
.container:nth-child(2) {
  animation: xfade 12s 4s infinite;
}
.container:nth-child(1) {
  animation: xfade 12s 8s infinite;
}

@keyframes xfade{
  17% {
    opacity:1;
  }
  45% {
    opacity:0;
  }
  92% {
    opacity:0;
  }
}
<div class="container">
  <div class="wrap">
    <div class="image" style="background-image: url('http://placekitten.com/450/300')">
      <img src="http://placekitten.com/450/300">
    </div>
    <div class="caption-wrap">
      <div class="caption right">
        moar kitty!
        text .. right aligned
      </div>
    </div>
  </div>
</div>

<div class="container">
  <div class="wrap">
    <div class="image" style="background-image: url('http://placekitten.com/500/440')">
      <img src="http://placekitten.com/500/440">
    </div>
    <div class="caption-wrap">
      <div class="caption">
        have a kitty!!1
        have a kitty!!1
        text .. left aligned
      </div>
    </div>
  </div>
</div>

<div class="container">
  <div class="wrap">
    <div class="image" style="background-image: url('http://placekitten.com/300/440')">
      <img src="http://placekitten.com/300/440">
    </div>
    <div class="caption-wrap">
      <div class="caption center">
        text .. centered
      </div>
    </div>
  </div>
</div>
Asons
  • 84,923
  • 12
  • 110
  • 165
  • 3
    Down voter, please comment so I get a chance to correct/clarify what/why. – Asons Feb 05 '16 at 20:25
  • Interesting.. I don't quite understand what the purpose of the background image + img element setup is. At least for me, switching between the two changes nothing – gandalf3 Feb 08 '16 at 21:25
  • @gandalf3 The hidden `img` makes the caption match image's width, the `background-image` is needed to scale the image properly. If you remove the `background-image` and make `img` visible and then resize the browser's width so it becomes smaller than the image's width, the image will be squeezed and loose its ratio. Resize the width on [this fiddle](https://jsfiddle.net/LGSon/akfhh789/5/) and you'll see. – Asons Feb 08 '16 at 21:34
2

Try utitlizing border property

#big_container {
  display: block;
  position: relative;
  width: 100%;
  padding-bottom: 40%;
  white-space: nowrap;
  overflow-x: scroll;
  overflow-y: hidden;
}
#big_container>div {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
}
.little_container {
  display: inline-block;
  height: 100%;
  width: 100%;
  text-align: center;
}
#big_container figure {
  display: block;
  height: 100%;
  margin: 0;
}
/* 
     set `border` to `45px` as background for `figacption`
     set `border-left`, `border-right`, `border-top` to `0px`  
*/
img {
  max-height: calc(100% - 40px);
  border: 45px solid #ddd;
  border-left: 0px;
  border-right: 0px;
  border-top: 0px;
}
/*
     set `figcaption` elements
     set `top` to `-50px` to center `content`
     in vertical middle of `border`,
     set `width` to `calc(100% / 3)` : number of `figure` elements
     set `left` to `calc(100% / 3)` : position `content` at 
     horizontal center of `:after` , `border`
*/
figcaption {
  top: -50px;
  display: block;
  position: relative;
  background-color: transparent;
  padding: 10px;
  line-height: 20px;
  box-sizing: border-box;
  width: calc(100% / 3);
  left: calc(100% / 3);
}
<div id="big_container">
  <div>
    <div class="little_container">
      <figure">
        <img src="http://placekitten.com/500/440" />
        <figcaption>have a kitty!!1</figcaption>
      </figure>
    </div>

    <div class="little_container">
      <figure>
        <img src="http://placekitten.com/450/400" />
        <figcaption>moar kitty!</figcaption>
      </figure>
    </div>

    <div class="little_container">
      <figure>
        <img src="http://placekitten.com/300/440" />
        <figcaption>too many kitty..</figcaption>
      </figure>
    </div>

  </div>
</div>

jsfiddle https://jsfiddle.net/601a5uqv/11/ , plnkr http://plnkr.co/edit/kHnA3GUTiGu3jm4tE8l4?p=preview

guest271314
  • 1
  • 15
  • 104
  • 177
  • Fantastic, thanks! This pretty much does what I want, though I can't say I'm happy with caption content being in the css and all.. I'll hold out for a more clean/extendable solution as long as I can, but if all else fails, you deserve a bounty just for sheer creativity ;) – gandalf3 Jan 31 '16 at 07:01
  • Are the `width` and `left` statement needed in figcaption? – Py. Feb 02 '16 at 08:47
  • @Py. `width` , `left` used to center text of `figcaption` element in relation to number of `figcaption` elements horizontally positioned. Could probably be set several different ways to achieve expected results. – guest271314 Feb 02 '16 at 14:34
  • @gandalf3 Adding `overflow:hidden` , `text-overflow:elissis` to `figcaption` element https://jsfiddle.net/601a5uqv/18/ – guest271314 Feb 03 '16 at 22:49
  • As you asked before for a fiddle, I made quite an update if you like to check. – Asons Feb 05 '16 at 18:55
1

In this fiddle, I've gotten the desired effect using different display rules, as well as calculating using the vh unit. I stripped out superfluous markup and CSS, but this should be a good starting point that you can use and build on.

Code:

#big_container {
  white-space: nowrap;
}
.little_container {
  display: inline-block;
  width: 100%;
}
figure {
  display: table;
  margin: 0 auto;
}
img {
  height: calc(100vh - 40px);
}
figcaption {
  display: table-caption;
  caption-side: bottom;
  background-color: #ddd;
  padding: 10px;
  line-height: 20px;
}
<div id="big_container">

  <div class="little_container">
    <figure>
      <img src="http://placekitten.com/500/440">
      <figcaption>
        have a kitty!!1
      </figcaption>
    </figure>
  </div>

  <div class="little_container">
    <figure>
      <img src="http://placekitten.com/450/400">
      <figcaption>
        moar kitty!
      </figcaption>
    </figure>
  </div>

  <div class="little_container">
    <figure>
      <img src="http://placekitten.com/300/440">
      <figcaption>
        too many kitty..
      </figcaption>
    </figure>
  </div>

</div>
RPL
  • 3,500
  • 2
  • 21
  • 28
0

There's a very little known CSS property that's made for what you require: min-content. What this does, is:

  • Uses intrinsic value, meaning it adapts to it's content.
  • Reduces the width of the container automatically so that an image fits inside it perfectly.
  • If text content needs to wrap inside the container, it should.

Read this article: Design From the Inside Out With CSS Min-Content

There is one thing I'm not crazy about is the prefixes:

width: -moz-min-content;
width: -webkit-min-content;
width: min-content;

I added some resets and commented on which styles are:

  • Optional
  • Recommended
  • Required

Important CSS

  figure {
     height: 100%;
     width: -moz-min-content;
     /* REQUIRED */
     width: -webkit-min-content;
     /* REQUIRED */
     width: min-content;
     /* REQUIRED */
  }

DEMO: https://plnkr.co/edit/6e1hYkK6lB2KVS3uvQy2?p=preview

BONUS: https://plnkr.co/edit/gahMFQScxQtkweo1G2jD?p=preview

I have made the captions longer and added the following CSS to show how you can wrap your captions and not breakout or extend the figcaption.

CSS Bonus

figcaption * {
  white-space: pre-wrap;
  /* RECOMMENDED */
}

Although not a requirement, I assume that in the near future you'll want to have captions that wrap without any hassle. I reviewed the others:

  • LGSon's will actually extend the image and figcaption, easily fixed with my solution I'm guessing.

  • guest271314's extends text overflow, I'm not sure what would fix that since it's not a container.

  • Ryan Litte's extends the figcaption, I believe that my solution could probably fix that as well.

zer00ne
  • 41,936
  • 6
  • 41
  • 68
  • Maybe you could put in a fiddle or something along those lines. This doesn't seem to produce the desired output in the stackoverflow snippet I'm seeing. – Py. Feb 02 '16 at 08:33
  • Not bad, though `min-content` doesn't work in Edge/IE and if you resize, the caption doesn't. – Asons Feb 02 '16 at 10:52
  • Strange.. For me, the demo doesn't work in FF. The bonus works in FF 43, but doesn't work in FF 45!? – gandalf3 Feb 03 '16 at 00:43
  • @gandalf3 Ok, both are fixed I had to remove the absolute positioned layer that's stretched at 0 in each direction. – zer00ne Feb 03 '16 at 02:31
  • Thanks for introducing me to the `min-content` keyword! I couldn't get it to work with an image which was scaled to fit inside the container though.. Is this possible? – gandalf3 Feb 07 '16 at 20:58
  • @gandalf is this image one of your own? Or is it an image of one of those adorable kittens? Images from placeholding sites I believe are limiting since you have to declare an absolute value for the dimensions. I do recall another obscure property that helps leverage *replaced* items like images, it's called [`object-fit`] (https://css-tricks.com/almanac/properties/o/object-fit/). Please provide me the particular image and conditions required if any so I can apply that and another idea from the best [resource](https://developer.mozilla.org/en-US/docs/Web/CSS/width) – zer00ne Feb 07 '16 at 21:14
  • I'd get on to beginning this update, but there's someone that is downvoting with possible multiple accounts (there's a pattern but I could be wrong)... – zer00ne Feb 07 '16 at 21:18
  • @zer00ne The idea is that it should work with any image of any dimension (scaling up/down as needed). I don't know who is downvoting or why, but rest assured, you have my upvote :) – gandalf3 Feb 07 '16 at 23:11
  • @zer00ne IMHO, you shouldn't write in your answer what's wrong with someone else's answers, do that in a comment for each answerer, and focus on explaining why yours is so good instead. Also I made an update where my caption does not extend the image anymore, which make your statement wrong, so you shouldn't wonder why you get down voted as that could also be a reason. – Asons Feb 08 '16 at 09:57
0

This seems like a good fit for flexboxes. They work in all the modern browsers (http://caniuse.com/#feat=flexbox) and in IE 10/11 with some special CSS as noted below.

Fiddle (For some reason the captions are stretching in IE on the Fiddle but it works in the browser.)

#big_container {
  position:absolute;
  top:0;
  right:0;
  bottom:0;
  left:0;
  padding-bottom: 40px;
  display: flex;
  flex-flow: horizontal;
  align-items: flex-start;
  justify-items: space-around;
  flex-wrap: no-wrap;

  /* For IE 10, 11 */
  display: -ms-flexbox;
  flex-direction: row;
  -ms-flex-wrap: nowrap;
}

.little_container {
  display:inline-block;
  height:100%;
  min-width: 100vw;
  text-align:center;
  display: flex;
  flex-flow: vertical;
  align-items: center;
  justify-content:center;
  flex-direction: column;

  /* For IE 10, 11 */
  display: -ms-flexbox;
  -ms-flex-wrap: nowrap;
  -ms-flex-direction: column;
  -ms-flex-pack: start;
  -ms-flex-align: center;
}


figure {
  width: -moz-min-content;
  width: -webkit-min-content;
  position: relative;
  text-align:center;
  display: flex;
  flex-direction: column;
  flex-wrap: no-wrap;
  align-items: stretch;
  justify-content:center;
  margin:auto;

  /* For IE 10, 11 */
  display: -ms-flexbox;
  -ms-flex-direction: row;
  -ms-flex-wrap: wrap;
  -ms-flex-pack: center;
  -ms-flex-align: center;
  -ms-flex-line-pack: center;
}

figure img {

  display:block;
  text-align:left;
  box-sizing: border-box;
  margin:0;
  max-height:calc(100% - 40px); /* subtract height of caption */
  -ms-flex: 0 0 calc(100%-40);
  display: block;

}

figcaption {
  display:block;
  text-align:left;
  box-sizing: border-box;
  margin:0;
  padding:10px;
  line-height:20px;
  background-color:#ddd;
  -ms-flex: 1 1 60%;
  max-width: 100%;

}

I took out the extra <div> inside #big_container that wrapped the smaller items because it did not seem necessary.

<div id="big_container">

  <div class="little_container">
      <figure>
        <img src="http://placekitten.com/500/440">
        <figcaption>
          I has kitty!!
        </figcaption>
      </figure>
  </div>

  <div class="little_container">
      <figure>
        <img src="http://placekitten.com/450/400">
        <figcaption>
          moar kitty!
        </figcaption>
      </figure>
  </div>

  <div class="little_container">
      <figure>
        <img src="http://placekitten.com/300/440">
        <figcaption>
          I has too many kitty. but I still need moar. Oh! No!!!!! I cannot get enuf!
        </figcaption>
      </figure>
  </div>
</div>

Also, if you don't already know about them, look into the CSS Viewport units vw and vh. http://caniuse.com/#feat=viewport-units

J. Nilles
  • 87
  • 5
  • I like that the caption will expand as needed when more text is added. Is there some way to get the image + caption to scale up and fill as much of the container as possible without changing the aspect ratio of the image? After playing around with this for a while I came up with [this](https://jsfiddle.net/arzp9hd5/4/), but the caption is overflowing instead of making the image smaller.. BTW the absolutely positioned `div` was part of a setup for making a container with a fixed aspect ratio (from [this answer](http://stackoverflow.com/a/10441480/2730823)), but can be ignored for this question. – gandalf3 Feb 07 '16 at 01:04
  • 2
    I don't think there is. Pure CSS does not give you any way to determine the size of the content. If you used Javascript, you could figure out the length of the caption. With that you could get a good idea about how many lines the caption would take and then you could adjust the size of the image accordingly. But even that will be complex because when you adjust the height of the image you will have less space across to fill with letters. – J. Nilles Feb 08 '16 at 12:59
  • @J.Nilles That was a perfect way to explain the resizing issue of the caption text, thanks. – Asons Feb 08 '16 at 13:31