1

I want to align two elements in such a way, that one sticks to the top, and one to the bottom of the viewport. I'm trying to achieve this with flexbox, but don't want to use absolute positioning.

Whatever I tried, didnt work: I found this question on stock overflow, and tried to implement those answers - yet I still can't achieve the wanted result. Can anybody point me in the right direction, or explain what I did wrong?

Below is my html / css, here you can find the code snippets on jsbin, with different stuff i tried.

All of the other stackoverflow questions I found regarding this topic didn't work for me either - I really don't get why. Any help would be really appreciated.

<html>
  <body>
    <div class="top">
      <p class="text">Top</p>
    </div>
    <div class="bottom">
      <p class="text">Bottom</p>
    </div>
  </body>
</html>
body {
  min-height: 100%;
  display: flex;
  flex-direction: column;
}

.top {
  flex-grow: 1;
}

.bottom {
  margin-top: auto;
}
Alessandro
  • 69
  • 6

2 Answers2

2

You could use CSS grid and just create 3 rows. Two with size of auto making the rows as big as they need to be and one of size 1fr meaning it takes the rest of the space. There is a great guide for CSS grid at CSS Tricks.

body {
  margin: 0px;
  padding: 0px;
  height: 100vh;
  /* CSS Grid */
  display: grid;
  /* 3 rows */
  grid-template-rows: auto 1fr auto; 
}

.bottom {
  background: red;
}

.top {
  background: red;
}
<body>
  <div class="top">
    <p class="text">Top</p>
  </div>
  <div>
    Content
  </div>
  <div class="bottom">
    <p class="text">Bottom</p>
  </div>
</body>
Mushroomator
  • 6,516
  • 1
  • 10
  • 27
  • Thanks a lot for the answer! I had thought about that, but figured that would be more complex than it should be, right? What really bugs me, is why it doesn't work in the first place - why is that, and should it work the way I wanted it to? – Alessandro Feb 05 '23 at 21:28
  • 1
    Well, it's basically 3 CSS properties to be set to get the expected result, so it doesn't get much simpler than that in CSS I'd say. Probably no way to get to the same result with a simpler solution. – Mushroomator Feb 05 '23 at 21:31
  • What's the reason though that my solution / the one proposed on a lot of other questions on stack overflow doesn't work? The question I linked above wants to achieve pretty much the exact same thing I do - yet none of the solutions given work for me – Alessandro Feb 05 '23 at 21:35
  • 1
    The reason your approach didn't work is that you used min-height on the body and didn't set the height of the html element to 100%. The minimum height of the body is the height of its parent element. If there isn't any content on your page then the html element won't have any height. It shrinks down to only take up as much space as is needed by its children. Set `html { height: 100%; )` and your jsbin works. That said, both of the answers here offer a better approach though. – jme11 Feb 10 '23 at 03:05
2

The CSS in the snippet contains a full explanation of what I did and why, so here just a summary:

  • min-height: 100% needs to be min-height: 100vh as with 100% body will only take up the summarized height of the direct child elements it contains.
  • Flexbox justify-content: space-between to move outermost child elements to either side of the flexbox container. With flex-direction: column that would be top and bottom of the container.
  • The above makes .top and .bottom CSS as-is obsolete.

Snippet

/* Demo, to better see space distribution */
* { outline: 1px dashed }

p {
    /* BEWARE: by default a <p> has 'margin: 1em 0'
               adding extra space above and below */
}

body {
    min-height: 100vh; /* from 100% */
    /* as 100% will make body only use up total height
       of elements it contains. 100vh will force it to
       fill the entire viewport height.
    */

    margin: 0; /* to remove HTML default 8px spacing */
    /* Margin adds to the total height of body
       causing the vertical scrollbar to appear
       when using 100vh */

    display: flex; flex-direction: column;

    /* moves two outer elements to either side */
    /* more elements get distributed inbetween */
    justify-content: space-between;
    /* I put in extra <div> between top and bottom
       to show what happens.
    */
}

.top {/* [OBSOLETE] */
    /* makes this element fill the entire screen */
/*    flex-grow: 1; /* probably unwanted, disabled */
}

.bottom {/* [OBSOLETE] */
/*    margin-top: auto; /* not required for effect */
    /* handled by flexbox parent justify property */
}

/*
    As body grows with content it can (and probably will)
    exceed 100vh, making .bottom move downward out of the 
    viewport. And when you scroll down, .top will move out
    of view.

    If you need either top/bottom permanently fixed you
    will have to use the position: fixed property
    on the elements instead of the Flexbox solution.

    - .top    { position: fixed; top: 0 }
    - .bottom { position: fixed; bottom: 0 }
*/
<div class="top">
    <p class="text">Top</p>
</div>

<div>
    <p>extra div &gt; p 1</p>
</div>

<div>
    <p>extra div &gt; p 2</p>
</div>

<div class="bottom">
    <p class="text">Bottom</p>
</div>
Rene van der Lende
  • 4,992
  • 2
  • 14
  • 25
  • Thank you so much! Your reply solved my issue. Your comment on the influence of margins on the document flow also explains why content overflowed even when body height was set to 100vh. Just to clarify - do margins on the paragraphs add to 100vh as well? As in, am I always better off using padding, if I want to avoid exceeding min-height beyond the viewport? – Alessandro Feb 14 '23 at 22:54
  • 1
    `margin` always adds to the total width/height of an element (e.g. `width: 100%; margin: 1rem` will yield a total width of `1rem + 100% + 1rem` because of left/right margin). Beware though **without** `* { box-sizing: border-box }` both `padding` and `border` will do too. Seach online for *MDN box-sizing* and *MDN box model* and **own** the knowledge!! While my personal preference, often you are indeed better off using 'padding'. When `margin` on a child makes it grow, try adding `padding` to its parent instead. Last thing: CSS Grid/Flexbox `...gap` act like `margin` as well. – Rene van der Lende Feb 15 '23 at 01:11
  • 1
    Thanks for all the tips! I had seen * { box-sizing: border-box; } on a lot of code before, but hadn't looked up it's function. I love the MDN web docs with it's well-laid out documentation. For now, I'm struggling a bit with javascript, but this works like a charm! – Alessandro Feb 18 '23 at 14:47