Manngo
Manngo

Reputation: 16291

CSS Flex Box: varying the number of items on each row

As an exercise, I am working on using Flex Box to implement the following layout:

┌───────────────────────────────┐
│ header                        │
└───────────────────────────────┘
┌─────┐ ┌───────────────────────┐
│ nav │ │ main                  │
│     │ │                       │
└─────┘ └───────────────────────┘
┌───────────────────────────────┐
│ footer                        │
└───────────────────────────────┘

My initial thought was to use:

body {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
}

but I’m not sure what to do about the rest. Currently I use:

header,footer {
    width: 100%;
}
nav {
    flex-basis: 0;
    flex-grow: 1;
}
main {
    flex-basis: 0;
    flex-grow: 4;
}

with a bit of extra fine tuning.

This works, but it seems contrived.

An alternative solution would be to wrap the nav and main element inside a div, but I am trying to avoid that as it reduces flexibility.

The question is, what is the most appropriate way to have a layout with one element on some rows, and multiple elements on others?

Upvotes: 0

Views: 1244

Answers (2)

VXp
VXp

Reputation: 12068

The point is in flex-basis which needs to be defined, i.e. different from the initial value of auto, in order to enable wrapping of additional elements, if the prefered way is doing it without the additional parent element, which is perfectly fine:

$('button:first-of-type').click(function(){
  $('<div>3</div>').insertAfter('.insertAfter');
});

$('button:last-of-type').click(function(){
  $('div:not(.first)').toggleClass('flexBasis');
});
body {
  display: flex;
  flex-wrap: wrap;
}

body > * {text-align: center}

header, footer {
  flex-basis: 100%;
  background: #f88;
}

nav {
  flex-basis: 20%;
  background: lightgreen;
}

main {
  flex-basis: 80%;
  background: lightblue;
}

.first {
  flex-basis: 33.33%;
  background: lightgray;
}

div {
  flex: 1; /* not enough */
  background: gray;
}

.flexBasis {
  flex: 1 25%; /* enough */
}

.insertAfter ~ div {color: #fff; background: #000}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<button>Add</button>
<button>Toggle flex-basis</button>

<header>Header</header>
<nav>Nav</nav>
<main>Main</main>
<div class="first">1</div>
<div class="first">1</div>
<div class="first">1</div>
<div>2</div>
<div>2</div>
<div>2</div>
<div class="insertAfter">2</div>
<footer>Footer</footer>

Upvotes: 2

Gerardo BLANCO
Gerardo BLANCO

Reputation: 5648

Two thing you should add:

1) Make the body display:flex to turn it into a flex container

2) Use the flex shorthand to indicate the flex grow and basis to the header and footer. By making the basis 100vw, you make does flex items 100% of the viewport width

flex: 1 100vw;

Hope this helps :)

body {
    display:flex;
    flex-direction: row;
    flex-wrap: wrap;
    background: red;
    margin:0;
}

header,footer {
    width: 100%;
    background: blue;
    flex: 1 100vw;
}
nav {
    flex-basis: 0;
    flex-grow: 1;
     background: green;
}
main {
    flex-basis: 0;
    flex-grow: 4;
     background: pink;
}
<body>
<header>&nbsp</header>
<nav>&nbsp</nav>
<main>&nbsp</main>
<footer>&nbsp</footer>
</body>

Upvotes: 0

Related Questions