Home > Enterprise >  Trying to overflow HTML <pre><code> tags instead of wrapping is breaking the flex spacin
Trying to overflow HTML <pre><code> tags instead of wrapping is breaking the flex spacin

Time:12-05

I'm applying CSS to the pre code selector in order to make styled code blocks,like you'd see on GitHub or elsewhere. I'm using flexbox for the layout, and I have two "panel" divs side-by-side inside of a "box" div, one of which has a code block (Which is just code inside of <pre><code> tags), and the "box" div is inside of a main "container" div.

The basic CSS I have is...

.*, *:before, *:after {
    margin: 0;
    padding: 0;
    box-sizing: inherit;
}

html {
    box-sizing: border-box;
}

body {
    display: flex;
    align-items: center;
    justify-content: center;
}

pre code {
    display: inline-block;
    overflow: auto;
    white-space: pre;
    padding: 1rem;
    margin: 1rem;
}

.container {
    display: flex;
    flex-direction: column;
    margin: 1rem;
    gap: 1rem;
}

.box {
    display: flex;
    flex-direction: row;
    gap: 1rem;
}

.panel {
    display: flex;
    flex-direction: column;
    flex: 0.5;
    padding: 1rem;
}

The two panels should be equal width, due to flex: 0.5, however the right panel expands to fit the block, rather than the block shrinking to fit the panel.

code block with white-space: pre

If I set white-space: pre-wrap on pre code, I get the desired layout behavior, but then of course the code is word-wrapped, which I don't want.

code block with white-space: pre-wrap

And of course, if I use white-space: pre and add a dedicated width to the pre code, I get the desired behavior, where the code block has a horizontal scrollbar. I can't use a dedicated width, because I need the block to fit any panel it's inside of.

Setting width: 100% on pre code does nothing at all, for some reason.

Just to make sure I wasn't causing this error myself by doing something somewhere else, I put together this snippet to confirm my issue (I did add some background colors and margins to make the containers visible):

https://codepen.io/evprkr/pen/poKQXJr

CodePudding user response:

CSS issues causing your problems:

  1. There is an easy to miss initial . (dot) in your first line of CSS .*, *:before, *:after { }. Therefore the border-box model is only applied to :before and :after pseudo elements. Oviously the same is true for margin and padding.
  2. None of the .panel ancestors have a width value set, this way flexbox cannot constrain child elements and will grow into infinity.
  3. flex: 0.5 (defaulting to flex: 0.5 1 0%) apparently has no effect, as it does not width flex-basis: 50%. In both cases the pre code will somehow not get triggered to overflow and therefore will not show a scrollable box. I cannot explain why, but it must be due to some W3C spec. However, the .panel width: 50% you declared eventually resolves this issue.
  4. Your combined use of margins with the various elements and gap produces seemingly unexpected overlapping of elements when resizing the browser/codepen. Even after removing the forementioned initial .

solution

  • ad 1) Remove the . (dot)
  • ad 2) Assign .container width: 100% to give flexbox a constraint to work with.
  • ad 3) Remove .panel flex: 0.5 and assign .panel width: calc(50% - 0.5rem). The calc(..) is required, because gap adds to the total width of the .panels, potentially causing them to overlap on resize. As your gap: 1rem adds 0.5rem to each of the two columns, that value needs to be subtracted from the width of each column.
  • ad 4) Like gap, margin adds to the total width of an element which would require you to subtract left and/or right margins from element widths to prevent them from overlapping other elements. The easiest way to circumvent having to add extra calc(..) in your CSS, move margin of an element to the padding of its direct parent element. This will not be true in all circumstances, but in this instance it works out just fine without changing the overall layout.

Bonus

For responsive behaviour:

  • Allow .box to wrap its child elements
  • Set some required minimum .panel width to force .box to wrap its child .panels. I chose 300px in this instance.
  • This requires the .panel elements to be allowed grow to the full 50% when not wrapping.

And hyphenate text for readability...

snippet

*, *:before, *:after {
    margin: 0;
    padding: 0;
    box-sizing: inherit;
}

html {
    box-sizing: border-box;
}

body {
    display: flex;
    align-items: center;
    justify-content: center;
}

pre code {
    background: #d7d7db;
    display: block;
    overflow: auto;
    white-space: pre;
    padding: 1rem;

}

.container {
    background: #0197f4;
    display: flex;
    flex-direction: column;
    padding: 1rem;
    width: 100%;
}

.box {
    background: #ff7538;
    display: flex;
    flex-flow: row wrap;
    gap: 1rem;
    padding: 1rem;
}

.panel {
    background: #fff;
    display: flex;
    flex-direction: column;
    padding: 2rem;

    /* Adjust for parent gap value */
    width: calc(50% - 0.5rem);

    /* Allow to grow and wrap when less than 300px */
    flex: 1;
    min-width: min(300px, 100%);
}

.text { hyphens: auto }
<div >
    <div >
        <div >
            <div >Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi accumsan mattis ullamcorper. Nulla tincidunt aliquam feugiat. Sed imperdiet tellus ligula, vehicula condimentum neque venenatis ut. Aenean varius elementum nulla, vehicula laoreet erat aliquam at. Nullam venenatis ex id tincidunt fringilla. Vivamus nec tellus sit amet leo tristique luctus. Cras elementum at diam at viverra. Phasellus tristique elementum velit id tincidunt. Nullam ullamcorper fringilla nulla sed ultricies.</div>
        </div>  
        <div >
            <pre><code>function SomeFakeFunction(first_argument, second_argument) {
    if (first_argument > second_argument) {
        return "first argument is greater than second argument";
    } else if (first argument < second_argument) {
        return "first argument is less than second argument";
    }
    
    return "first argument and second argument are equal (or invalid but this is just filler code so i don't care to write anymore honestly)"
}</pre></code>
        </div>
    </div>
</div>

  • Related