Setting a negative margin which is a percentage of its height. Pure CSS
Asked Answered
K

5

15

I have a series of images, which have different captions. each of these captions needs to appear to float halfway over the bottom of the image. Here is a mockup of how this looks and also how I have structured it in divs. because there needs to be space between the bottom of the caption and whatever div is beneath, the caption cannot be absolutely positioned.

I am aware this could be done in javascript but this would be a hack so I'd prefer to find a CSS only solution.

View image of my wireframe mockup

...and here is the code so far: JS FIDDLE

.outer {
  position: relative;
  width: 320px;
  margin-bottom:24px // this needs to be the space between the bottom of the caption and the next image or whatever div is beneath.
}

.image {
  background-color: blue;
  width: 320px;
  height: 380px;
  position: relative;
  border-radius: 5px;
}

.caption {
  position: relative;
  margin-top: -24px;
  /* THIS NEEDS TO BE 50% of the Captions variable height */
}

.card {
  margin-right: 25%;
  margin-left: 5%;
  position: relative;
  background-color: #FFFFFF;
  padding: 24px;
  line-height: 24px;
}
<div class="outer">
  <div class="image">
  </div>
  <div class="caption">
    <div class="card">
      Lorem ipsum dolar sit amet consectetur idipiscine ipsumd lorem ipsum dolar sit amet. a
    </div>
  </div>
</div>
Kela answered 7/12, 2017 at 14:54 Comment(1)
FWIW, you don't need to apply display: block; to a <div>. They are block level elements.Teddman
K
5

Unfortunately, it turns out this is completely impossible with just CSS... Despite many people believing that there is no valid use case for having negative padding, I think this is.

So... what I ended up doing was using an ::after block element, that covers the bottom half of the captions background. However, as this means I couldn't have rounded corners or have the entire image showing I also added a javascript over-ride. This queries half of the captions height, and applies it as a padding bottom value to the container. see the fiddle and code below:

https://jsfiddle.net/todd_143/nawLywc9/5/

<div class="outer">
  <div class="image">
  </div>
  <div class="caption">
    <div class="block">
      Lorem ipsum dolar sit amet consectetur idipiscine ipsumd lorem ipsum dolar sit amet.
    a</div>
  </div>
</div>

body {
  background-color:#FFFFFF;
}
.outer {
    display:block;
    position:relative;
    width:320px;
}

.image {
    background-color:blue;
    width:320px;
    height:350px;
    display:block;
    position:relative;
    border-radius:5px;
}

.caption {
    position:absolute;
    bottom:0; right:0; left:0;

}

.caption::before {
    background-color: #FFFFFF;
    content:"";
    position:absolute;
    top:50%; left:0; right:0; bottom:-1px;
    background-color#FFFFFF;
    display:block;
}

.block {
  position:relative;
  background-color:#FFFFFF;
  padding:24px;
  left:5%;
   line-height:24px;
  width: 60%;
  border-radius: 5px;
  margin: 0 auto;
  display:inline-flex;
  box-shadow: inset 0px -3px 0px 0px rgba(0,0,0,0.05);
}

$(document).ready(function() {
    promoCaption();
});

$(window).resize(function() {
    promoCaption();
});

function promoCaption() {
    //allow caption to sit half way on bottom edge of promos while keeping their ratio. Purely cosmetic
    $(".outer").each(function() {
        // get caption height
        var captionHeight = ($(this).find(".caption").height());
        // get caption offset by halfing its height
        var captionOffset = captionHeight / 2;
        // apply caption offset as padding bottom to the promo container
        $(this).css("padding-bottom", captionOffset);
    });
}

Frustrating though, as I do not like using javascript for anything structural. But for this to work I couldn't find any other way.

Kela answered 11/12, 2017 at 16:22 Comment(0)
C
9

Just use position: absolute and transform property to do the trick.

Here is an example: https://jsfiddle.net/nawLywc9/1/

The .block classs must be like this:

.block {
    position:relative; // This is important
    background-color:#FFFFFF;
    padding:24px;
    line-height:24px;
    bottom: -50%; // This is important
    transform: translateY(-50px); // This is important
    width: 60%;
    border-radius: 5px;
    margin: 0 auto; // Only if you need to be centered
}

Here is the preview: https://jsfiddle.net/nawLywc9/1/

Comradery answered 7/12, 2017 at 15:5 Comment(3)
THanks for this Alejandro... but by absolutely positioning the caption, how do I determine the space between the outer and whatever div is beneath... I have updated the fiddle to show this. there needs to be a fixed 24px space between the bottom of the caption and the next image block. jsfiddle.net/todd_143/nawLywc9/3Kela
well, doing that using just css its literally impossible because i assume that the height of the caption is different from each block (am i right?) so the only way to do it is put a certain height to caption and use the css trick to put triple dots after certain words (like "lorem ipsum dolor amet...")Comradery
I feared that would be the case, but I was hoping someone might have a css trick up their sleeve ;) . so frustrating as I hate using javascript for structural stuff. thanks for your help all the same.Kela
M
9

I've achieved this with:

/* Required to expand element to height required. */
position: relative;
/* Move up by 50% of own Height. */
transform: translateY(-50%);
Mikvah answered 13/10, 2020 at 18:50 Comment(1)
This is a very nice solution. However, there is one small problem: there is a gap below the moved caption that is 50% of it's height. If this could be removed, e.g. using a negative margin-bottom, it would be perfect. Is it possible to set such a margin in relation to the height?Impudent
K
5

Unfortunately, it turns out this is completely impossible with just CSS... Despite many people believing that there is no valid use case for having negative padding, I think this is.

So... what I ended up doing was using an ::after block element, that covers the bottom half of the captions background. However, as this means I couldn't have rounded corners or have the entire image showing I also added a javascript over-ride. This queries half of the captions height, and applies it as a padding bottom value to the container. see the fiddle and code below:

https://jsfiddle.net/todd_143/nawLywc9/5/

<div class="outer">
  <div class="image">
  </div>
  <div class="caption">
    <div class="block">
      Lorem ipsum dolar sit amet consectetur idipiscine ipsumd lorem ipsum dolar sit amet.
    a</div>
  </div>
</div>

body {
  background-color:#FFFFFF;
}
.outer {
    display:block;
    position:relative;
    width:320px;
}

.image {
    background-color:blue;
    width:320px;
    height:350px;
    display:block;
    position:relative;
    border-radius:5px;
}

.caption {
    position:absolute;
    bottom:0; right:0; left:0;

}

.caption::before {
    background-color: #FFFFFF;
    content:"";
    position:absolute;
    top:50%; left:0; right:0; bottom:-1px;
    background-color#FFFFFF;
    display:block;
}

.block {
  position:relative;
  background-color:#FFFFFF;
  padding:24px;
  left:5%;
   line-height:24px;
  width: 60%;
  border-radius: 5px;
  margin: 0 auto;
  display:inline-flex;
  box-shadow: inset 0px -3px 0px 0px rgba(0,0,0,0.05);
}

$(document).ready(function() {
    promoCaption();
});

$(window).resize(function() {
    promoCaption();
});

function promoCaption() {
    //allow caption to sit half way on bottom edge of promos while keeping their ratio. Purely cosmetic
    $(".outer").each(function() {
        // get caption height
        var captionHeight = ($(this).find(".caption").height());
        // get caption offset by halfing its height
        var captionOffset = captionHeight / 2;
        // apply caption offset as padding bottom to the promo container
        $(this).css("padding-bottom", captionOffset);
    });
}

Frustrating though, as I do not like using javascript for anything structural. But for this to work I couldn't find any other way.

Kela answered 11/12, 2017 at 16:22 Comment(0)
C
1

So you're looking to always have this caption adhere to the bottom of the image, while sitting 50% below of the parent container based on the captions height?

.outer {
  display:block;
  position:relative;
  width:320px;
}

.image {
  background-color:blue;
  width:320px;
  height:380px;
  display:block;
  position:relative;
  border-radius:5px;
}

/* Swapped the caption div to absolute, so it's always adhered to the bottom of the 'outer' container */

.caption {
  display:block;
  width: 100%;
  position: absolute;
  left: 0;
  bottom: 0;
}

/* Setup the 'block' div to do the actual '50%' manuevering based on the height of the  'caption' container with 'transform: translate(50%)' */

.block {
  transform: translateY(50%);
  background-color:#FFFFFF;
  padding:24px;
  line-height:24px;
}

I've setup a jsfiddle here to demonstrate the changes @ https://jsfiddle.net/evanbriggs/v7rxqtcz/. Let me know if this is what you were thinking.

Cohobate answered 7/12, 2017 at 15:4 Comment(1)
Thanks Evan, but that fiddle link took me back to the original. but I don't think absolute would work anyway... There needs to be a specific margin space between the bottom of the caption, and whatever object is beneath it. here is a fiddle showing this problem jsfiddle.net/todd_143/4uo5h5d5Kela
F
-1

Try Changing Margin-top to -25%.

.caption {
display:block;
position:relative;
margin-top:-25%; 
}
Fervid answered 7/12, 2017 at 15:2 Comment(3)
Thanks Jonny, but not sure how that will get it to be 50% of the captions height... I have updated the fiddle here, with your suggestion, unless I mis-understood? jsfiddle.net/todd_143/nawLywc9/2Kela
So you want to center the caption no matter the height of it, You want it centered and responsive to the text?Fervid
This cant work : #4982980Maeda

© 2022 - 2025 — McMap. All rights reserved.