Search code examples
csscontainersoverflow

CSS container width issue, doesn't play nice with "overflow"?


I believe this is my first time posting here. I've been working in HTML and CSS on and off for 20+ years, but I'm a bit of a noob when it comes to modern HTML5 and CSS, so I don't know if this is me misunderstanding something or if it's a bug of some kind. (Probably the former.)

Here I have a mock-up of an interface layout. In general, it's supposed to be a full-screen interface (no scrolling), within which I want a menu panel that stretches across its container and scrolls horizontally.

<html>
    <style>
        .fullscreen { width: 100%; height:100%; position: absolute; top: 0px; left: 0px; margin: 0px; }
        .border { border: solid black 4px; padding: 8px; }
        .menuitem { display: inline-block; width: 300px; height: 300px; border: solid blue 1px; margin: 8px; }
    </style>
    <div class=fullscreen style="display: flex;"> <!-- full screen container-->
        <div style="padding: 8px; flex-grow: 1; display: flex;"> <!-- screen padding - doesn't play nice with fullscreen -->
            <div class=border style="min-width: 100px; margin-right: 8px;"></div> <!-- left menu -->
            <div class=border style="flex-grow: 1;"> <!-- right content container, grows to fill remaining space -->
                <div class=border style="max-width: 100%;"> <!-- intermediate container for the scrolling view, should be as wide as permissible -->
                    <div style="white-space: nowrap; overflow-x: auto; max-width: 100%;"> <!-- horizontally scrolling view -->
                        <!-- arbitrary contents of the scrolling view -->
                        <div class=menuitem></div>
                        <div class=menuitem></div>
                        <div class=menuitem></div>
                        <div class=menuitem></div>
                        <div class=menuitem></div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</html>

The item of focus is the aforementioned scrolling view. I'm trying to limit its size to remain within its parent container, but it seems that its contents, although clipped, are causing the parent container to grow well off of the screen.

I've tried a variety of combinations of nesting containers, limiting sizes to varying amounts, etc.

One observation is that this only happens if I set the scrolling view to have a percentage max-width. If I specify a number of pixels, then it works as I would expect. This indicates to me that the layout size is calculated before the clipping of the scroll view occurs. I was hoping that the intermediate container (commented in the code) would help to fix that.

Similarly, if I change the scrolling view to have a much smaller percentage max-width (say, 10%), that div does indeed become smaller, but, bizarrely, the parent containers still behave as though the full width was present.

In short, a max-width set in pixels causes the parents to size as I would expect, but has unintuitive results when it's set to any percentage.

So, am I misunderstanding something about the concept of percent widths? Or perhaps there's a simple CSS attribute that will resolve this?


Solution

  • There are plenty of things that I'd change in your code. Let me put your code fixed first and then I'll explain what's wrong.

    <html>
    <head>
    <style>
      .fullscreen { width: 100vw; height: 100vh; overflow: hidden; display: flex; }
      
      .border { border: solid black 4px; padding: 8px; }
      
      .menuitem { display: inline-block; width: 300px; height: 300px; border: solid blue 1px; margin: 8px; }
      
      * {
        box-sizing: border-box;
      }
    
      body {
        margin:0;
      }
    
      .outer-container {
        display: grid;
      }
    
      .inner-container {
        white-space: nowrap; overflow-x: auto; max-width: 100%;
      }
    </style>
    </head>
      <body>
        <div class="fullscreen"> <!-- full screen container-->
            <div style="flex-grow: 1; display: flex; gap: 8px; margin: 8px"> <!-- screen padding - doesn't play nice with fullscreen -->
                <div class="border" style="min-width: 100px;"></div> <!-- left menu -->
                <div class="border" style="flex-grow: 1;"> <!-- right content container, grows to fill remaining space -->
                    <div class="border outer-container"> <!-- intermediate container for the scrolling view, should be as wide as permissible -->
                        <div class="inner-container"> <!-- horizontally scrolling view -->
                            <!-- arbitrary contents of the scrolling view -->
                            <div class="menuitem"></div>
                            <div class="menuitem"></div>
                            <div class="menuitem"></div>
                            <div class="menuitem"></div>
                            <div class="menuitem"></div>
                            <div class="menuitem"></div>
                            <div class="menuitem"></div>
                            <div class="menuitem"></div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        </body>
    </html>
    

    First of all, I'd use absolute positioning as you are moving it to another z axis which is not needed in this case. Next, use vw/vh units in case you want to have always full device width and height (especially height can be quirky). Use gap instead of margin to make spaces between containers in flexbox (you've already used display: flex so you can make use of gap).

    Finally, the main thing that solves your problem: display: grid. The difference between flex and grid in simple words is the way they calculate the space for elements. The element with display: grid gives rules how children has to be drawn and how their dimensions need to be calculated, with flexbox it is kind of opposite. Children take what they need, so max-width in this case means take 100% of width that you need. In general to make it working as you want, you have to put any width that terminates calculation, so that's why pxs were also working - they have finite number.