Search code examples
htmlcsscss-calc

Alignment of fixed width item in percentage based parent


At the moment I have an outer container with a maximum width of 1600 pixels, which can shrink below that based on screen width.

A have an inner container within the outer with a maximum width of 1200 pixels.

I have a flex box within the outer container, which has two children, one set at 60% width and the other at 40% width. I have a div within the 60% parent, whereby I don't want the left-hand edge to extend beyond the 1200 pixel container above.

The problem is, I cannot get the div within the 60% parent to align to the left of the 1,200 pixel container. It works when the screen width is above 1,600 pixels or below 1,200 pixels, but not in-between.

Here's an image to show what I'm trying to achieve:

enter image description here

I've come up with a solution using media queries, calc() and 100vw, however it's contrived and flakey namely because 100vw includes the scrollbar width.

Below is a code snippet of what I have so far (you'll need to run this in full screen mode, and resize your screen to get an idea of what is happening).

Is there a simple calculation or solution that I'm missing?

body {
        margin: 0;
        padding: 10px;
    }
    .outer {
        max-width: 1600px;
        margin: 0 auto;
        border: solid 1px green;
        color: green;
    }

    .inner {
        max-width: 1200px;
        margin: 0 auto 20px auto;
        border: solid 1px black;
        color: black;
    }

    .flex {
        display: flex;
        color: red;
    }

    .flex > div:first-child {
        width: 60%;
        border: solid 1px red;
        box-sizing: border-box;
    }

    .flex > div:last-child {
        width: 40%;
        border: solid 1px red;
        box-sizing: border-box;
    }

    .align {
        border: solid 1px blue;
        margin-left: auto;
        /* max-width: 757px;Doesn't work */

        color: blue;
    }

    @media screen and (min-width: 1220px) { /* Extra 20 pixels for body padding */
        .align {
            margin-left: calc((100vw - 1200px - 20px) / 2);
        }
    }

    @media screen and (min-width: 1620px) { /* Extra 20 pixels for body padding */
        .align {
            margin-left: 198px;
        }
    }
<body>
    <div class="outer">
        max width 1600px
        <div class="inner">max width 1200px</div>
        <div class="flex">
            <div>
                width 60%
                <div class="align">left edge to align with 1200px above</div>
            </div>
            <div>width 40%</div>
        </div>
    </div>
</body>


Solution

  • You are almost good. Replace the 100vw with the container width which is in your case 100%/0.6 since the parent element is 60% (0.6*100%)

    body {
      margin: 0;
      padding: 10px;
    }
    
    .outer {
      max-width: 1600px;
      margin: 0 auto;
      border: solid 1px green;
      color: green;
    }
    
    .inner {
      max-width: 1200px;
      margin: 0 auto 20px auto;
      border: solid 1px black;
      color: black;
    }
    
    .flex {
      display: flex;
      color: red;
    }
    
    .flex>div:first-child {
      width: 60%;
      border: solid 1px red;
    }
    
    .flex>div:last-child {
      width: 40%;
      border: solid 1px red;
    }
    
    .align {
      border: solid 1px blue;
      margin-left: auto;
      color: blue;
    }
    
    @media screen and (min-width: 1220px) {
      .align {
        margin-left: calc((100%/0.6 - 1200px) / 2);
      }
    }
    
    @media screen and (min-width: 1620px) {
      .align {
        margin-left: 200px;
      }
    }
    
    * {
      box-sizing:border-box;
    }
    <body>
      <div class="outer">
        max width 1600px
        <div class="inner">max width 1200px</div>
        <div class="flex">
          <div>
            width 60%
            <div class="align">left edge to align with 1200px above</div>
          </div>
          <div>width 40%</div>
        </div>
      </div>
    </body>

    A code that you can simplify like below using clamp()

    body {
      margin: 0;
      padding: 10px;
    }
    
    .outer {
      max-width: 1600px;
      margin: 0 auto;
      border: solid 1px green;
      color: green;
    }
    
    .inner {
      max-width: 1200px;
      margin: 0 auto 20px auto;
      border: solid 1px black;
      color: black;
    }
    
    .flex {
      display: flex;
      color: red;
    }
    
    .flex>div:first-child {
      width: 60%;
      border: solid 1px red;
    }
    
    .flex>div:last-child {
      width: 40%;
      border: solid 1px red;
    }
    
    .align {
      border: solid 1px blue;
      margin-left: clamp(0px,(100%/0.6 - 1200px) / 2,200px);
      color: blue;
    }
    
    
    
    * {
      box-sizing:border-box;
    }
    <body>
      <div class="outer">
        max width 1600px
        <div class="inner">max width 1200px</div>
        <div class="flex">
          <div>
            width 60%
            <div class="align">left edge to align with 1200px above</div>
          </div>
          <div>width 40%</div>
        </div>
      </div>
    </body>

    Or using width/max-width:

    body {
      margin: 0;
      padding: 10px;
    }
    
    .outer {
      max-width: 1600px;
      margin: 0 auto;
      border: solid 1px green;
      color: green;
    }
    
    .inner {
      max-width: 1200px;
      margin: 0 auto 20px auto;
      border: solid 1px black;
      color: black;
    }
    
    .flex {
      display: flex;
      color: red;
    }
    
    .flex>div:first-child {
      width: 60%;
      border: solid 1px red;
    }
    
    .flex>div:last-child {
      width: 40%;
      border: solid 1px red;
    }
    
    .align {
      border: solid 1px blue;
      max-width: 100%;
      margin-left: auto;
      width: calc(100% - ((100%/0.6 - 1200px) / 2));
      color: blue;
    }
    
    
    
    * {
      box-sizing:border-box;
    }
    <body>
      <div class="outer">
        max width 1600px
        <div class="inner">max width 1200px</div>
        <div class="flex">
          <div>
            width 60%
            <div class="align">left edge to align with 1200px above</div>
          </div>
          <div>width 40%</div>
        </div>
      </div>
    </body>

    And why not padding on the container. A good one line solution since padding is by default clamped to 0 (cannot be negative)

    body {
      margin: 0;
      padding: 10px;
    }
    
    .outer {
      max-width: 1600px;
      margin: 0 auto;
      border: solid 1px green;
      color: green;
    }
    
    .inner {
      max-width: 1200px;
      margin: 0 auto 20px auto;
      border: solid 1px black;
      color: black;
    }
    
    .flex {
      display: flex;
      color: red;
    }
    
    .flex>div:first-child {
      width: 60%;
      border: solid 1px red;
      padding-left: calc((100% - 1200px) / 2);
    }
    
    .flex>div:last-child {
      width: 40%;
      border: solid 1px red;
    }
    
    .align {
      border: solid 1px blue;
      color: blue;
    }
    
    
    
    * {
      box-sizing:border-box;
    }
    <body>
      <div class="outer">
        max width 1600px
        <div class="inner">max width 1200px</div>
        <div class="flex">
          <div>
            width 60%
            <div class="align">left edge to align with 1200px above</div>
          </div>
          <div>width 40%</div>
        </div>
      </div>
    </body>