0

I'm trying to build myself a flexbox with the following design:

My html looks like this (and I would like to NOT change this):

<div class="wrapper">
  <div class="page-header">Heading</div>
  <h3 class="right_1">right_1</h3>
  <div class="right_2">right_2</div>
  <div class="left">form element</div>
  <h3 class="right_3">right_3</h3>
  <div class="right_4">right_4</div>
</div>

This is the standard layout of the joomla contact page. I want to change the design though without altering html/php code.

Is that possible using flexbox?

And can I use @media queries to make right_1 - right_4 to move under left on mobile view (< 800px or example)?

I myself cannot get it to work, I always end up with right_1 - right_4 next to each other instead of them stacking to the total height of the left portion.

Alkahna
  • 441
  • 7
  • 21
  • Nope, just by flexbox alone that is not possible. You will have to try out the experimental CSS grid. What flexbox does is that it gives you an easy means of distributing elements flexibly, but doesn't do that bi-axially (as you want it in a grid). – Terry May 18 '17 at 15:10
  • ok, thx for that info. Im open for anythin other than editing html/php structure of joomla. – Alkahna May 18 '17 at 15:13

3 Answers3

3

The layout you want cannot be achieved with flexbox. The reasons are explained here:

However, the layout is relatively simple with CSS Grid.

There are, in fact, multiple methods for building the layout with Grid. I'll use the grid-template-areas property, which allows you to lay out your elements using ASCII art.

.wrapper {
  display: grid;
  height: 100vh;
  grid-template-columns: 1fr 1fr;
  grid-template-areas: 
     " heading heading "
     "  left_ right1   "
     "  left_ right2   "
     "  left_ right3   "
     "  left_ right4   "
}

.page-header { grid-area: heading; }
.right_1     { grid-area: right1;  }
.right_2     { grid-area: right2;  }
.right_3     { grid-area: right3;  }
.right_4     { grid-area: right4;  }
.left        { grid-area: left_;   } /* "left" is a pre-defined CSS keyword,
                                         so it may not work */
@media ( max-width: 800px ) {
  .wrapper { 
     grid-template-columns: 1fr;
     grid-template-areas: 
        " heading "
        "  left_  "
        "  right1 "
        "  right2 "
        "  right3 "
        "  right4 "
     }
}

/* non-essential decorative styles */
.page-header { background-color: red; }
.right_1     { background-color: chartreuse; }
.right_2     { background-color: aqua; }
.right_3     { background-color: skyblue; }
.right_4     { background-color: black; color: white; }
.left        { background-color: cornsilk; }
body         { margin: 0; }
.wrapper > * {
  font-weight: bold;
  font-size: 1.5em;
  border: 1px solid black;
  margin: 0;
  display: flex;
  justify-content: center;
  align-items: center;
}
<div class="wrapper">
  <div class="page-header">Heading</div>
  <h3 class="right_1">right_1</h3>
  <div class="right_2">right_2</div>
  <div class="left">form element</div>
  <h3 class="right_3">right_3</h3>
  <div class="right_4">right_4</div>
</div>

jsFiddle demo

In essence, here's how it works:

  1. We establish a block-level grid with display: grid.
  2. With grid-template-columns: 1fr 1fr we are telling the grid to create two columns. The fr unit tells the container to consume available space. It's similar to flexbox's flex-grow property. So both columns will share the full width of the container.
  3. The grid-template-areas property allows you to lay out named grid areas (that you have defined) to create a visual representation of your layout using ASCII art.
  4. In the media query for smaller screens, we remove one column and re-order the grid areas.

Browser Support for CSS Grid

  • Chrome - full support as of March 8, 2017 (version 57)
  • Firefox - full support as of March 6, 2017 (version 52)
  • Safari - full support as of March 26, 2017 (version 10.1)
  • Edge - full support as of October 16, 2017 (version 16)
  • IE11 - no support for current spec; supports obsolete version

Here's the complete picture: http://caniuse.com/#search=grid

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
  • that sounds pretty good, I will throw that code in and check ;) – Alkahna May 18 '17 at 16:10
  • works pretty nice I must say. The only thing I left out was `height: 100vh;` from the `.wrapper{..}` at the top because it caused the contents of `
    ` to overflow which looked aweful, but just getting rid of `height: 100vh;` does the trick ;) Only thing left now is to get it to work on edge/IE somehow (have not tried yet)
    – Alkahna May 19 '17 at 07:24
  • Thats why I opted for flexbox first, it has support for IE/Edge and opera mini/android browser. – Alkahna May 19 '17 at 07:29
  • Another thing I'm working on now is to limit the size of the elements of the right. There are some big gaps between `right_1` & `right_2` as well as `right_3` & `right_4` (1/3 being headings and 3/4 being the corresponding text) – Alkahna May 19 '17 at 08:16
  • @Alkahna That is caused by the `h3` elements, which has a default margin. Set their margin to 0 and it will go away – Asons May 20 '17 at 04:17
  • I got it working with grid thank you but I also want a flexbox solution as a fallback because of better browser support (edited my question above) – Alkahna May 24 '17 at 20:52
2

Here is one way to solve that, based on the existing markup and CSS Flexbox.

The left needs to be absolute positioned for desktop view and the page-header need a fixed height.

If you don't want to set a fixed height, you'll need a script that takes care of the height calculation

html, body {
  margin: 0;
}
.wrapper {
  position: relative;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}
.wrapper * {
  padding: 0 10px;
  margin: 0;
  border: 1px solid black;
  box-sizing: border-box;  
}
.page-header {
  height: 70px;
  width: 100%;
  background: red;
}
.right_1, .right_2, .right_3, .right_4 {
  flex: 1;
  width: 50%;
  margin-left: 50%;
  background: lightblue;
}
.left {
  position: absolute;
  left: 0;
  top: 70px;
  width: 50%;
  min-height: calc(100vh - 70px);
  background: yellow;
}
@media only screen and (orientation: portrait) {
  .wrapper * {
    width: 100%;
    margin: 0;
  }
  .left {
    position: static;
    flex: 1;
    min-height: auto;
    order: -1
  }
  .page-header {
    order: -2;
  }
}
<div class="wrapper">
  <div class="page-header">Heading</div>
  <h3 class="right_1">right_1</h3>
  <div class="right_2">right_2</div>
  <div class="left">form element</div>
  <h3 class="right_3">right_3</h3>
  <div class="right_4">right_4</div>
</div>
Asons
  • 84,923
  • 12
  • 110
  • 165
  • I tested your solution as well and found that the media query needs some adjustsments to work propperly: `@media only screen and (max-width: 768px) { .wrapper * { width: 100% !important; margin: 0 !important; } .left { position: static !important; flex: 1; min-height: auto !important; order: -1 } }` content of `.wrapper` is overflowing `.wrapper` but I will get on with fixing that – Alkahna May 19 '17 at 07:54
  • @Alkahna Of course it does, as I didn't do any such adjustments. The CSS Grid is better, as you get a more responsive solution. Its down side is of course browser support. If you are going to use Flexbox, I recommend you add a small script that deal with the positioning/sizing of the `left`. I mean, it is an okay trade off as it doesn't add much workload on the browser – Asons May 19 '17 at 07:59
  • @Alkahna Btw, the script will of course not be needed if you can control the sizing using media queries – Asons May 19 '17 at 08:07
  • I noticed, as I already have other media queries I will just continue to use them and avoid as much script as possible. Do you have an idea on my Edit of my original question? – Alkahna May 24 '17 at 20:54
  • 1
    @Alkahna That is a completely new question, so rollback that edit and create a new one with your new requirements. And accept the one here that solved this question the best. – Asons May 24 '17 at 21:01
0

Probably the easiest way to change the order of div elements, you can use Jquery for this purpose. For example:

$("#element1").before($("#element2"));

Eventually:

CSS:

#blocks {
    display: box;
    box-orient: vertical;
}
#b1 {
    box-ordinal-group: 2;
}
#b2 {
    box-ordinal-group: 3;
}

HTML:

<div id="blocks">
    <div id="b1">Block 1</div>
    <div id="b2">Block 2</div>
    <div id="b3">Block 3</div>
</div>

It should work.