102

I have a div called .side-el which I would like to have in a position: fixed; behavior, but as soon as I apply position fixed the width alternates from the right one. The right width would be the one set by flexbox. How can I achieve this goal?

.container {
  -webkit-align-content: flex-start;
  align-content: flex-start;
  -webkit-align-items: flex-start;
  align-items: flex-start;
  display: -webkit-flex;
  display: flex;
  -webkit-flex-direction: row;
  flex-direction: row;
  -webkit-flex-wrap: wrap;
  flex-wrap: wrap;
  -webkit-justify-content: flex-start;
  justify-content: flex-start;
  
  * {
    box-sizing: border-box;
    -webkit-flex-grow: 1;
    flex-grow: 1;
    -webkit-flex-shrink: 0;
    flex-shrink: 0;
  }
}


.main-el {
  box-sizing: border-box;
  padding:0 2em;
  width: 70%;
}

.side-el {
  box-sizing: border-box;
  width: 30%;
}
<div class="container" style="background-color: blue; height: 100px;">
  <div class="main-el">
    <div  style="background-color: red; height: 1000px;">content</div>
  </div>
  <div class="side-el" >
    <div style="background-color: red; height: 100px;">content</div>
  </div>
</div>
Daniel Schmidt
  • 11,605
  • 5
  • 38
  • 70

9 Answers9

98

Here's a way to do this inspired by bootstrap:

.fixed-top {
  display: flex;
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
}

This gives your flex-box room to breathe and do it's flex-box thing. If your flex-direction is column, you could use top, left, bottom instead.

This works because when you give an element a fixed position and a left and right of 0 or a top and bottom of 0, the element is stretched to fill the space from left to right, or top to bottom. That in turn allows a flex-box to use the amount of space you would expect without position fixed.

Wylliam Judd
  • 9,935
  • 2
  • 26
  • 36
61

You can't.

As explained by the CSS2.1 spec:

Absolutely positioned boxes are taken out of the normal flow.

And the Flexible Box Layout spec confirms that:

An absolutely-positioned child of a flex container does not participate in flex layout. However, it does participate in the reordering step (see order), which has an effect in their painting order.

(Emphasis mine)

Oriol
  • 274,082
  • 63
  • 437
  • 513
49

@Daniel , I know this is very late but ... while the accepted answer is correct, I don't feel it's very helpful. I had the same question (which is how I came across this post), and the solution I think I'll go with is to wrap the position fixed element within the flex element. Here's a (very ugly) example

Relevant Markup

  <aside class="Layout-aside" ng-class="{'isCollapsed': collapsed}" ng-controller="AsideCtrl">
    <div class="Layout-aside-inner">
      <button ng-click="collapsed = !collapsed">
        <span ng-show="collapsed">&gt;</span>
        <span ng-hide="collapsed">&lt;</span>
      </button>
      <ul class="Layout-aside-content">
        <li ng-repeat="i in items">{{i}}</li>
      </ul>
    </div>
  </aside>

Relevant CSS

.Layout-aside {
  order: 0;
  min-width: 140px;
  width: 140px;
  background-color: rgba(0, 255, 0, .4);
  transition: width .4s, min-width .4s; 
}
.Layout-aside.isCollapsed {
  min-width: 25px;
  width: 25px;
}

.Layout-aside-inner {
  position: fixed;
}

.Layout-aside.isCollapsed .Layout-aside-inner {
  width: 25px;
}

.Layout-aside.isCollapsed .Layout-aside-content {
  opacity: 0;
}
o4ohel
  • 1,779
  • 13
  • 12
25

position:sticky was mentioned by Juozas Rastenis above but without code example. Here's a minimalist example:

* {
  box-sizing: border-box;
}

body {
 display: flex;
 margin: 0;
}

nav {
 width: 20%;
 height: 100vh;
 top: 0; /* this is required for "sticky" to work */
 position: sticky;
 background: lightblue;
 padding: 1rem;
}

main {
 height: 3000px; /* cause scroll */
 background: lightpink;
 flex-grow: 1;
 padding: 1rem;
}
<body>
 <nav>
  sidebar here
 </nav>

 <main>
  content here
 </main>
</body>
Roman
  • 5,888
  • 26
  • 47
  • 1
    Thanks! Literally the only solution which worked for me. Using `fixed` always messed the `
    ` up (was "underneath" ``).
    – CodingTil Jun 17 '21 at 19:15
18

You can achieve it with a css alternative position: sticky

It acts great but the only problem is browser support (June 2018): https://caniuse.com/#feat=css-sticky

Hope it gets better soon.

Juozas Rastenis
  • 265
  • 3
  • 9
6

A far simpler solution would be to use overflow-y:scroll and height: 100vh on the main-el container. This will give the appearance of fixed position to the side-el container without resorting to position: fixed.

Greg Beaver
  • 1,062
  • 11
  • 10
  • 4
    the only problem is that the scrollbar on the center content element looks absolutely horrible, want it on the side like the browser window – PirateApp Jan 15 '18 at 06:04
  • .its bad using position fixed for multi level div, this on keeping everything neat in my site! – Jones G Oct 24 '20 at 18:35
2

You are saying you want position:fixed;-like behavior that plays together with flexbox. As mentioned in the accepted answer, applying this property to an element drops it out of the normal flow, so this isn't really possible.

If what you want is to have a fixed sidebar .side-el and a scrollable content box .main-el as the items of a flex container, here's how you might do this:

  1. Disable scrolling in the flex container's parent; let's assume it's <body>, as you don't provide div.container's parent. Also, hard-set it's height to viewport-height (100vh) so that no part of the body's box remains outside view (imagine the body's box normally extending beyond your screen to contain the entire document; you don't want that, if you are to disable the ability to move the viewport via scrolling).
  2. Set the flex container's (.container) height to that of it's parent.
  3. Selectively re-enable scrolling for the content box (.main-el).

In CSS:

body{
  overflow: hidden;
  height: 100vh;
}
.container {
  display: flex;
  height: 100%;
}
.main-el {
  overflow-y: auto;
}
odnes
  • 31
  • 1
1

You can achieve this without position: fixed; by just adding overflow: auto; and height: 100%; to the flex-item that contains the long content:

.container {
   display: flex;
}

.main-el {
  padding:0 2em;
  width: 70%;
        overflow: auto;
  height: 100%;
}

.side-el {
  width: 30%;
}     
<div class="container" style="background-color: blue; height: 300px;">
  <div class="main-el">
    <div  style="background-color: red; height: 1000px;">content</div>
  </div>
  <div class="side-el" >
    <div style="background-color: red; height: 100px;">content</div>
  </div>
</div>
TebokaRoa
  • 21
  • 3
0

I had the same issue, I actually just found a way to have flex-box, a width for the nav bar, and center it while in a fixed position.

nav {
    display: flex;
    height: 50px;
    width: 90%;
    left: 5%;
    position: fixed;
}

I wanted to be able to have a flex-box nav bar in a fixed position but centered. So what I did was do the left 5% since that's equal to half of the 10% width left over. Try it out, it might help you! :)

Adrian D.
  • 514
  • 1
  • 5
  • 12