0

I'm building a website for myself with a two-column content layout, where the columns have a 1:2 aspect ratio, and I am trying to avoid using Grid for something that I believe Flexbox can more than easily handle. However, all of my attempts to force a wrap from the narrow left column to the wide right column using flex-basis: 100%; do not work without an explicit, non percentage height set for the parent element. I don't know what to do. I've already used this article and referenced multiple questions for solutions, and literally nothing has worked.

I'm using Firefox 72 and this is supposed to work in recent versions of Firefox.

:root {
  --bodywidth: 80vw;
  --flexbasis: calc(var(--bodywidth) / 8);
  --spacebasis: calc(var(--flexbasis) / 12);
  --columnwidth: calc(var(--bodywidth) / 3);
}


/* https://tobiasahlin.com/blog/flexbox-break-to-new-row/ */

hr.break {
  border: none;
  margin: 0;
  padding: 0;
  flex-basis: 100%;
  flex-grow: 1;
}

hr.row.break {
  height: 0;
}

hr.col.break {
  width: 0;
}

main {
  display: flex;
  flex-flow: column wrap;
  /* height: 100%; /* << DOES NOT WORK */
  /* height: 100vw; /* << Works perfectly fine, but NOT ideal */
}


/* vv As a bonus, these rules somehow make everything 2 column widths wide when only the stuff AFTER the break should be that wide */

main :not(.break) {
  min-width: var(--columnwidth);
  width: 100%;
}

main hr.break+* {
  width: calc(var(--columnwidth) * 2);
}
<main>
  <section>
    <h1>About</h1>
    <p>Blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah.</p>
  </section>

  <section>
    <h1>Disclaimer</h1>
    <p>Here there be naughty things!!!</p>
  </section>

  <!-- The amount of content on both sides of the break varies, as do the dimensions of the sections -->
  <hr class="col break" />

  <article class="blog">
    <h1>Blog Entry</h1>
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum eleifend molestie orci. Donec pellentesque viverra magna, nec viverra velit laoreet non. Etiam blandit erat nulla, semper faucibus eros rhoncus vel.</p>
  </article>

</main>

If I have to, I can hold my nose and use Grid and make it work, but it's not ideal by any stretch of the imagination and would require a whole lot of extra CSS to make it work. I would much rather use Flexbox if anyone has solutions.

G-Cyrillus
  • 101,410
  • 14
  • 105
  • 129
Calyo Delphi
  • 329
  • 1
  • 3
  • 16
  • 1
    Without a height limitation on the flex container, how would the items know where to wrap? https://stackoverflow.com/a/43897663/3597276 – Michael Benjamin May 01 '20 at 19:22
  • You can improve your markup, wrapping content for the small column into an – G-Cyrillus May 01 '20 at 19:43
  • The idea for doing it this way is to make the content more flexible for mobile layouts. I.E. if the viewport is below a certain resolution with a portrait aspect ratio, then those break elements can be given a flex basis of 0, specific elements in the aside can be chucked in different places using the order property, and everything shuffled together into just one column. – Calyo Delphi May 01 '20 at 20:43
  • okay, so what would be the reorder scenario you need here ? grid and a breakpoint seems to be what you need actually – G-Cyrillus May 01 '20 at 20:50
  • For example, that content disclaimer is the last item in the list on the sidebar. If everything reflows into one column for phones, the disclaimer should jump to the last item overall. And so on and so forth depending on the rest of the content on the page. – Calyo Delphi May 01 '20 at 21:07
  • i setted an answer with a demo of the grid and media querie switch, you will find it much easier to manage i believe. – G-Cyrillus May 01 '20 at 21:36
  • I do appreciate that answer, and it's... close but no cigar to my use scenario. If there was a way to use grid a la flexbox—I.E. define only the columns, ignore horizontal tracks entirely, and allow everything to pack dense like flexbox—then it would be perfect. But I may have to just explicitly specify the sizes of everything. :/ – Calyo Delphi May 01 '20 at 21:46
  • As an addendum, it seems I'm not the only person pining for this sort of functionality in Grid as well. It's an open issue in the W3C CSSWG issue tracker on github: https://github.com/w3c/csswg-drafts/issues/1373 – Calyo Delphi May 01 '20 at 21:46

2 Answers2

1

grid and mediaquerie is , IMHO, a good way to manage the swhitching from a 1 column layout to a 2 columns layout.

Demo of the idea :

:root {
/* possible use of var here */
--col1 : 1fr;
--col2 : 2fr;
}
main {
  display: grid;
  grid-template-columns: var(--col1) var(--col2);
}
main> * {
  border:solid 1px;
  margin:2px;
}
section {
  grid-column:1;
}
article {
  grid-column:2;
  grid-row:1 / 10;
}

/* breakpoint at 768px */
@media screen and (max-width: 768px) {
  main {
    display: flex;
    flex-flow: column;
  }
  main section + section {/* best is to use an id */
    order: 1;
  }
}
<main>
  <section>
    <h1>About</h1>
    <p>Blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah.</p>
  </section>
  <section><! -- that one might deserve an id to easy reorder its position -->
    <h1>Disclaimer</h1>
    <p>Here there be naughty things!!!</p>
  </section>
  <article class="blog">
    <h1>Blog Entry</h1>
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum eleifend molestie orci. Donec pellentesque viverra magna, nec viverra velit laoreet non. Etiam blandit erat nulla, semper faucibus eros rhoncus vel.</p>
  </article>

</main>
G-Cyrillus
  • 101,410
  • 14
  • 105
  • 129
  • I'mm'a upvote and accept this answer because it definitely works in certain situations! Doesn't work for mine, unfortunately, but it's still a workaround nonetheless. The solution I figured out used some javascript to set an explicit height for the container element, computed from the heights of the content inside. – Calyo Delphi May 02 '20 at 00:37
0

I was able to figure out a hackish workaround using javascript. This is probably unique to my use case, so I'm not going to flag this as the accepted answer, but I'm publishing it here in case anyone needs it for their own uses! This hack works with the exact same HTML+CSS as above:

var timerID = 0;

// This function saves CPU cycles and user sanity when resizing the browser window!
// It just destroys and creates a timeout as the resize event gets rapid fired
function resizeDelay() {
    if(timerID) clearTimeout(timerID);
    timerID = setTimeout(recomputeMainHeight,500);
}

// THIS function does all the magic!
function recomputeMainHeight(mainElts) {
    // Get the computed margins of the elements in the columns
    var margin = parseInt(window.getComputedStyle(mainElts[0]).marginBottom) * 2;

    // Find the index of the break element
    var breakIndex = 0;
    while(mainElts[breakIndex].nodeName != "HR") breakIndex++;

    /*  The height of the left column is equal to:
        The box bottom coordinate of the break's preceding sibling, MINUS
        The box top coordinate of the first child node in the container, PLUS
        The margin width of the elements in the flex TIMES the number of items BEFORE the break
    */
    var leftColContentHeight =
        mainElts[breakIndex].getBoundingClientRect().bottom -
        mainElts[0].getBoundingClientRect().top +
        margin * breakIndex;
    /*  The height of the right column is equal to:
        The box bottom coordinate of the last child node in the container, MINUS
        The box top coordinate of the break's following sibling, PLUS
        The margin width of the elements in the flex TIMES the number of items AFTER the break
    */
    var rightColContentHeight =
        mainElts[mainElts.length - 1].getBoundingClientRect().bottom -
        mainElts[breakIndex + 1].getBoundingClientRect().top +
        margin * (mainElts.length - 1 - breakIndex);

    //  Set the greater of the two values as the height of the container
    document.querySelector("main").style.height =
        (leftColContentHeight > rightColContentHeight ?
        leftColContentHeight :
        rightColContentHeight) + "px";
}

//  This function sets the widths of everything dependign on their relation to the break
function bootstrap() {
    //  Fetch a list of all fist-gen children of the container
    var mainElts = document.querySelectorAll("main > *");

    var pastBreak = false;
    //  Loop through the NodeList...
    for(elt of mainElts) {
        //  If the loop finds the break element, set the flag and continue
        if(elt.nodeName === "HR") {
            pastBreak = true;
            continue;
        }
        //  If the loop is past the break, set the width to the wide column, else the narrow column
        if(pastBreak) elt.style.width = "calc(var(--columnwidth) * 2)";
        else elt.style.width = "var(--columnwidth)";
    }

    //  Call the function that works the hack magic above
    recomputeMainHeight(mainElts);
}

//  Some event listeners
window.addEventListener('load',bootstrap);
window.addEventListener('resize',resizeDelay);

CAVEATS:

  1. I have not tested this in IE or Safari/WebKit; I've read that Safari/WebKit reports different values for getComputedStyle() so if you want to support Safari/WebKit you'll have to adapt this accordingly. I'll probably have to do so myself when I start testing this page on my fruityPhone and fruityPad.
  2. A small visual bug will be present between Firefox and Chrom(e|ium). Firefox DOES NOT account for margins when elt.getBoundingClientRect() is called and all the numbers are computed. Chrom(e|ium) DOES. I chose to adapt for Firefox only and accept an excess of gutter at the bottom of the page because setting the container height slightly too high is preferable to slightly too low and having stuff overlap each other and the last thing I want to do is wrestle with browser sniffing.
Calyo Delphi
  • 329
  • 1
  • 3
  • 16