anthony-dandrea
anthony-dandrea

Reputation: 2813

Pin element (flex item) to bottom of container

I have a container to be the full window height/width. Inside this container I want a form that's vertically & horizontally centered. Below, I also want a piece of copy on the bottom baseline of the container. Similar to the shitty illustration below. Right now I am only able to have them both centered vertically and can't find a nice way to make the bottom copy pin itself to the bottom of the container.

---------
|       |
|       |
|<form> |
|       |
|<copy> |
---------

.container {
  background-color: #eee;
  height: 100vh;
  width: 100vw;
  padding: 1em;
  display: flex;
  text-align: center;
  justify-content: center;
  flex-direction: column;
}

form {
  
}

.bot {
  align-self: flex-end;
}
<div class="container">
  <form>
    <input type="text" />
    <button type="submit">submit</button>
   </form>
  <p class="bot">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Omnis quae quisquam neque cupiditate adipisci magnam facilis, distinctio suscipit possimus hic voluptatibus in illo est id alias unde sapiente ab eius.</p>
</div>

Upvotes: 19

Views: 33626

Answers (5)

chazsolo
chazsolo

Reputation: 8484

Updated Answer

With auto margins you can group or pack flex items together without extra markup, or shift elements around by applying a margin in the opposite direction (as seen in my original answer). In this case, simply set margin: auto on the form; this will inform the flex container to center the form within all available space remaining:

.flex-container {
  display: flex;
  flex-direction: column;
  text-align: center;
  height: 150px;
  width: 400px;
  background: #e7e7e7;
}
 
form {
  margin: auto;
}

p {
  margin: 0;
}
<div class="flex-container">
  <form>
    <input type="text" />
    <button type="submit">submit</button>
  </form>
  <p class="bot">
    Lorem ipsum dolor sit amet
  </p>
</div>

See the other answers here for more insight:

In CSS Flexbox, why are there no "justify-items" and "justify-self" properties?

Can't scroll to top of flex item that is overflowing container


Original Answer

A clever way to "pin" a flex child is to give it an auto margin in the direction opposite of where you want it to go. In your case, you'd want to set

p.bot {
    margin-top: auto;
}

and the paragraph will shift to the bottom of the parent flex container. It works pretty well with simple layouts such as this...

html,
body {
  margin: 0;
  padding: 0;
}
.container {
  background-color: #eee;
  height: 100vh;
  width: 100vw;
  display: flex;
  text-align: center;
  justify-content: center;
  flex-direction: column;
  position: relative;
}
form {
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}
.bot {
  margin-top: auto;
}
<div class="container">
  <form>
    <input type="text" />
    <button type="submit">submit</button>
  </form>
  <p class="bot">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Omnis quae quisquam neque cupiditate adipisci magnam facilis, distinctio suscipit possimus hic voluptatibus in illo est id alias unde sapiente ab eius.</p>
</div>

Edit Note, I've also made the form a nested flexbox within the .container and set it's height to 100%, which is basically doing the same thing. Michael_B has a good explanation in the comments.

Upvotes: 37

Michael Benjamin
Michael Benjamin

Reputation: 371889

Here's the problem you're having:

You have set the flex-direction to column. That means you've shifted the main axis to vertical, and the cross axis to horizontal.

The align-* properties work along the cross axis. So when you're telling .bot to align-self: flex-end, you're actually telling it to shift right, not down.

But it will not shift right in this case because the item is occupying the full width of the container and, therefore, has no space to move. However, if you limit it's width... http://jsfiddle.net/t7hap87o/

In wanting to pin <copy> to the bottom of the screen, flexbox has a bit of a limitation: There is no property similar to align-self for the main axis.

There are several other options to get the job done. You can nest flexboxes in the current container, or remove .bot from this container and place it in another.

Flexbox also allows for absolutely-positioned flex children. Try this:

.container {
    position: relative; /* establish nearest positioned ancestor for abs. positioning */
}

.bot {
    position: absolute;
    bottom: 0;
}

DEMO

Note: A margin-top: auto on .bot won't work in the current HTML structure. It would, in fact, pin the target element to the bottom. But it would also shove the centered element all the way to the top.

Upvotes: 5

misterManSam
misterManSam

Reputation: 24702

The align-self: flex-end; doesn't have the desired affect, because the flex children are lined up as columns.

One easy way to line this up as desired, without adding any extra markup, is to:

  • move the footer element out of the .container

    <div class="container"></div>
    <p class="bot"></p>
    
  • give the body (or another wrapper, if required) display: flex so that the .container and footer are flex children (siblings).

  • give .container a flex property of flex: 1 1 100%;. This will cause it to grow and shrink from its initial value of 100%. The footer is pushed down, but its height is accommodated for when the container shrinks.

  • give the footer a flex property of flex: 1 1 auto. This will now be pushed down the bottom, but will increase height with more contents.

Read more about the flex property over on the MDN.

Example

* {
  margin: 0;
  padding: 0;

}
body {
  display: flex;
  flex-direction: column;
  height: 100vh;
  width: 100vw;
}
.container {
  display: flex;
  flex: 1 1 100%;
  flex-direction: column;
  justify-content: center;
  text-align: center;
  background: #EEE;
}
.bot {
  flex: 1 1 auto;
  text-align: center;
  background: #EEE;
  padding: 1em;
}
<div class="container">
  <form>
    <input type="text" />
    <button type="submit">submit</button>
  </form>
</div>
<p class="bot">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Omnis quae quisquam neque cupiditate adipisci magnam facilis, distinctio suscipit possimus hic voluptatibus in illo est id alias unde sapiente ab eius.</p>

Upvotes: 3

Stickers
Stickers

Reputation: 78736

You can create a pseudo element by using :before on the container, to make it as three sections, and use justify-content: space-between;

* {
    margin: 0;
}
.container {
    background-color: #eee;
    height: 100vh;
    width: 100vw;
    display: flex;
    justify-content: space-between;
    flex-direction: column;
    text-align: center;
}
.container:before {
    content: '';
}
<div class="container">
    <form><input type="text" /> <button type="submit">submit</button></form>
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Omnis quae quisquam neque cupiditate adipisci magnam facilis, distinctio suscipit possimus hic voluptatibus in illo est id alias unde sapiente ab eius.</p>
</div>

jsfiddle

Upvotes: 8

ratherblue
ratherblue

Reputation: 2108

Change the flex-direction back to column, set one of the containers to width: 100% and use flex-wrap: wrap to achieve this.

Something like this (and it will work in Firefox and IE 10+ with vendor prefixes):

.container {
  background-color: #333;
  width: 100%;
  height: 100vh;
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: center;
}

.footer {
  width: 100px;
  height: 20px;
  margin-top: -20px; /* set the margin top to negative what the 
                        height is so the form is perfectly centered */
  background-color: #900;
  color: #fff;
  text-align: center;
}

.form-wrapper {
  width: 100%; /* needs to be 100% to force the items to wrap */
  height: 50px;
  background-color: #009;
  
  /* center the form inside the wrapper */
  display: flex;
  align-items: center;
  justify-content: center;
}
<div class="container">
  <div class="form-wrapper">
    <form>
      <input type="text">
      <button type="submit">submit</button>
    </form>
  </div>
  <div class="footer">footer content</div>
</div>

Upvotes: 1

Related Questions