I ran into a fundamental difference between flexbox and box-model layouts (bootstrap 3 vs bootstrap4). CSS grid layouts - as seen in frameworks like Bootstrap and Foundation - use the rules of CSS to help layout and align parts of a Web page. Flexbox is fundamentally different from CSS grid systems because flexbox uses rules baked into the browser and not the cascading rules of CSS.

Flexbox vs Box Model

When using flexbox to layout components, the browser must natively support flexbox. When you set a component to display:flex some default assumptions are made of the *immediate* children.

Flexbox seems to work from a top-down approach when deciding on available width, whereas box model was a bottom up approach.

Django-CMS and flexbox

Django-CMS likes to wrap your content in extra divs to allow for a structured view as well as double clicking an element to edit it. This wrapper div injects itself between your flex layout div and its immediate children.

See how the col-md-8 is only taking up 66% of the parent div? That's because flexbox seems to work from a top-down approach when deciding on available width, whereas box model was a bottom up approach.

If I add "flex-grow:1" to the django-cms injected div, then each div will take up half of the row - which means the first col-md-8 will only take up 66% of 50%!

Fix it with CSS3 display:content

So, I went through a number of ways to attempt a fix, including *not* wrapping the django divs around my content, and hacking the CMS JS to use ('.cms_plugin + *') as the selector to get the direct sibling node of the django cms_plugin node. This did not work.

What I needed to do was somehow remove the cms_plugin div from the rendering pipeline. I thought maybe display:inline or position:absolute would remove it from the calculation of available space without touching the core JS.

But, then I stumbled upon the value display: contents. This will make the box model for this div use whatever its own children use when it comes to calculating the box model.

If you look closely at the above image, you can see that I'm hovering over the cms_plugin node and it does not have any highlighted outline in the browser.

display: contents effectively removes this django-cms injected node from the rendering pipeline.

The only thing you have to worry about is make sure that your CSS comes *after* the cms.core.js file or else you'll have to use !important, and then the structure view won't work.

Video

I realize this blog post doesn't explain everything very well, maybe this video will show you what I mean.

Update

In more recent versions of Django-CMS (like 3.2) you need the following CSS:
.cms-dialog form label, .cms-plugin,
.cms_plugin {
    display:contents !important;
}