2

I'm using flexbox to create a basic layout for a web application. I want there to be a menu across the top and, below that, a primary content area on the left and a secondary content area on the right, both of which vertically fill the space left over below the menu. If just the content areas are included in the HTML, the stretch covers everything. When I include the menu, however, I end up with a lot of white space between the menu and content areas.

In the JS fiddle, I added a little JavaScript to remove the menu when you click on either of the links to give a better idea as to how much white space (1rem) I'd like between the menu and the two content areas.

Can this be achieved using flex? Thanks!

$(document).ready(function() {
  $("a").click(function() {
    $(this).closest(".main-menu").remove();
  })
})
html {
  height: 100%;
}

body {
  display: flex;
  min-height: 100%;
  width: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  flex-wrap: wrap;
  align-content: stretch;
}

.main-menu {
  width: 90%;
  margin: 1rem calc(5% - 1px) 1rem calc(5% - 1px);
  padding: 1rem;
  border: 1px dashed black;
  align-self: flex-start;
}

.main-menu ul {
  margin: 0;
  border: 0;
  padding: 0;
  display: inline;
}

.main-menu ul li {
  margin: 0;
  border: 0;
  padding: 0;
  display: inline;
}

.primary-stuff {
  margin: 1rem 1rem 1rem calc(5% - 1px);
  width: calc(75% - 1rem - 1px);
  border: 1px dashed black;
}

.secondary-stuff {
  margin: 1rem calc(5% - 1px) 1rem 1rem;
  width: calc(15% - 1rem - 1px);
  border: 1px dashed black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="main-menu">
  <ul>
    <li><a>Link 1</a></li>
    <li><a>Link 2</a></li>
  </ul>
</div>
<div class="primary-stuff"></div>
<div class="secondary-stuff"> </div>

<div style="position:absolute;background:#FFC; width: calc(5% - 1px); height: 100%; left: 0;"></div>

<div style="position:absolute;background:#FFC; width: calc(5% - 1px); height: 100%; right: 0; top: 0;"></div>

<div style="position:absolute;background:#FFC; height: 1rem; width: 100%; right: 0; top: 0;"></div>

<div style="position:absolute;background:#FFC; height: 1rem; width: 100%; right: 0; bottom: 0;"></div>

Fiddle:

https://jsfiddle.net/don01001100/kdg04ubr/

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
Becca Dee
  • 1,530
  • 1
  • 24
  • 51

2 Answers2

1

You might find that the Grid Layout is a better fit for this type of layout, as it provides a cleaner method of arranging elements in the way you require, without the need for additional HTML markup.

You could achieve the layout you require using CSS-grid as follows:

html {
  height: 100%;
}

body {
  
  /* Cause grid to fill vertical space */
  height:100%;
  
  /* Prevent overflow due to padding */
  box-sizing:border-box;
   
  /* Use grid display type */
  display: grid;

  /* Tells grid to cause second row to fill 
     avaible/remaining vertical space */
  grid-template-rows: auto 1fr;

  /* Define the grid layout, in terms of areas
     that are distributed between 3 colums and
     2 rows */
  grid-template-areas: 
    "menu menu menu" 
    "primary primary secondary";
  
  /* Specify spacing between grid elements */
  grid-gap: 1rem;
  
  margin: 0;
  padding: 1rem;
  background:grey;
}

.main-menu {
  background: pink;
  
  /* Accociate the main-menu with the menu area
     of your grid-template-areas defined above */
  grid-area: menu;
}

.main-menu ul {
  margin: 0;
  padding: 0;
  display: inline;
}

.main-menu ul li {
  margin: 0;
  padding: 0;
  display: inline;
}

.primary-stuff {
  background: lightblue;
  /* Accociate the primary-stuff with the primary area
     of your grid-template-areas defined above */
  grid-area: primary;
}

.secondary-stuff {
  background: lightgreen;
  /* Accociate the secondary-stuff with the secondary area
     of your grid-template-areas defined above */
  grid-area: secondary;
}
<div class="main-menu">
  <ul>
    <li><a>Link 1</a></li>
    <li><a>Link 2</a></li>
  </ul>
</div>
<div class="primary-stuff">
  primary content
</div>
<div class="secondary-stuff">
  secondary content
</div>
Dacre Denny
  • 29,664
  • 5
  • 45
  • 65
1

You can't really do this with flexbox because of the way align-content works with wrap.

However, the layout is simple with CSS Grid layout:

body {
  display: grid;
  grid-template-columns: 75% 1fr;
  grid-template-rows: auto 1fr;
  grid-gap: 1rem;
  min-height: 100vh;
}

nav {
  grid-column: 1 / -1;
}

/* non-essential decorative styles */
body             { margin: 0; padding: 1rem 2rem; }
nav              { background-color: lightgreen; padding: 1rem; }
.primary-stuff   { background-color: lightblue; }
.secondary-stuff { background-color: orange; }
*                { box-sizing: border-box; }
<nav>
  <a>Link 1</a>
  <a>Link 2</a>
</nav>
<div class="primary-stuff"></div>
<div class="secondary-stuff"> </div>

Also, all those calculations you have for margins are not necessary. Here's an easy solution that will simplify your code: Flexbox: 4 items per row

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701