skip to Main Content

I’m trying to set the width of the striped area in my drawing below, so that it falls within the bounds of the site content. The width of the content is 80%, with a maximum width of 1080px, and centered (illustrated below in purple). The columns are variable widths, but together always fill the width of the page – I used 75%/25% below for argument’s sake.

What is the algorithm to achieve this? I have tried I number of things, including:

width: calc(80vw / <column width>)

but that’s way off. I want the CSS width assignment to work for any column size (I’m using hubl to create a for loop in the CSS – so I am able to use variables.. similar to SCSS)

enter image description here

2

Answers


  1. Say the container’s width is always 100% (or rather 100vw), in that case

    stripedWidth = 25vw     // but...
    

    would suffice. But, since the container’s width is limited to a max-width (of 1080px), keeping the striped element width at 25vw would make it too large – as we incremented the viewport width.

    Therefore the question is:

    "By how much do we need to subtract from 25vw (stripes width)"?

    Setting the striped element’s width to 25vw and slowly resizing the viewport the answer would become immediately visible and obvious:

    enter image description here

    we need to reduce that 25vw by exactly one margin’s width (that one space between the container edge and the viewport’s edge).

    margin = (viewport.width - container.width) / 2
    

    Solution:

    .striped {
      width: calc(25vw - (100vw - 100%) / 2);
    }
    

    Example:

    * { margin: 0; box-sizing: border-box; }
    
    body {
      background: linear-gradient(90deg, #ddd 75%, #ccc 75%);
      overflow: auto;
      resize: both;
      margin: auto;
    }
    
    .container {
      margin: auto;
      max-width: 480px; /* DEMO ONLY, you use 1080px */
      border: 2px solid green;
    }
    
    .striped {
      margin-left: auto;
      width: calc(25vw - (100vw - 100%) / 2);
      /* min-width: 50px; /* Uncomment if necessary */
      min-height: 4rem;
      background: repeating-linear-gradient(160deg,#0000,#0000 10px,#f80 10px,#f80 15px);
    }
    <div class="container">
      <div class="striped"></div>
    </div>
    
    Resize the viewport to test .striped width

    Tip:

    if you cannot use body but you have another parent which width does not match 100vw, then use container-type: size; on that element, and change vw units from the above code into cqw (container-query units) – and will work the same.

    Login or Signup to reply.
  2. There are two scenarios we need to include in the calculation.

    1. Wide screens, when the content width is constrained to 1080px
    2. Narrow screens, when the content width is 80%

    The screenshot below illustrates the setup:

    • yellow represents the viewport width (100vw)
    • pink represents the left column (75vw)
    • red represents the right column (25vw)
    • lime represents the content (the minimum of 1080px and 80vw)
    • the white striped area is the width you are wanting to calculate, which is the portion of the content which would overlap the right column

    enter image description here

    So for scenario 1, the width of the striped area is the width of the red area minus the width of the white area underneath the red area. To get the width of the white area we subtract the width of the lime area from the viewport width, then divide by two (because there are white areas on both sides). The equation for this scenario is therefore:

    stripeWidth = redWidth - ((yellowWidth - limeWidth) / 2)
    

    For scenario 2 it’s simpler, because we know that the white area is simply 10vw, so the equation for this scenario is:

    stripeWidth = redWidth - 10vw = 25vw - 10vw = 15vw
    

    For the overall equation we just need to take whichever of these two results has the smaller value. The CSS representation of the equation uses both min() and calc():

    .striped {
      width: min(15vw, calc(25vw - ((100vw - var(--content-width)) / 2)));
    }
    

    Here is a working snippet to demonstrate:

    const f = document.querySelector('.full')
    const l = document.querySelector('.left')
    const r = document.querySelector('.right')
    const c1 = document.querySelector('.c1')
    const s = document.querySelector('.striped')
    
    const displayWidths = () => {
      f.innerHTML = f.offsetWidth
      l.innerHTML = l.offsetWidth
      r.innerHTML = r.offsetWidth
      c1.innerHTML = c1.offsetWidth
      s.innerHTML = s.offsetWidth
    }
    
    addEventListener('resize', displayWidths)
    
    displayWidths()
    body {
      margin: 0;
      display: grid;
      grid-template-columns: 3fr 1fr;
      --content-width: 1080px;
    }
    
    .full, .left, .right, .c1, .c2, .striped {
      display: flex;
      justify-content: center;
    }
    
    .full {
      grid-column: 1 / span 2;
      background: yellow;
    }
    
    .left {
      background: pink;
    }
    
    .right {
      background: red;
      color: white;
    }
    
    .c1, .c2 {
      grid-column: 1 / span 2;
      width: min(var(--content-width), 80%);
      justify-self: center;
    }
    
    .c1 {
      background: lime;
    }
    
    .c2 {
      background: cyan;
      align-self: end;
      justify-content: end;
    }
    
    .striped {
      color: black;
      width: min(15vw, calc(25vw - ((100vw - var(--content-width)) / 2)));
      background: linear-gradient(135deg, rgb(255 255 255 / 1) 5%, rgb(255 255 255 / 0) 5%, rgb(255 255 255 / 0) 10%, rgb(255 255 255 / 1) 10%, rgb(255 255 255 / 1) 15%, rgb(255 255 255 / 0) 15%, rgb(255 255 255 / 0) 20%, rgb(255 255 255 / 1) 20%, rgb(255 255 255 / 1) 25%, rgb(255 255 255 / 0) 25%, rgb(255 255 255 / 0) 30%, rgb(255 255 255 / 1) 30%, rgb(255 255 255 / 1) 35%, rgb(255 255 255 / 0) 35%, rgb(255 255 255 / 0) 40%, rgb(255 255 255 / 1) 40%, rgb(255 255 255 / 1) 45%, rgb(255 255 255 / 0) 45%, rgb(255 255 255 / 0) 50%, rgb(255 255 255 / 1) 50%, rgb(255 255 255 / 1) 55%, rgb(255 255 255 / 0) 55%, rgb(255 255 255 / 0) 60%, rgb(255 255 255 / 1) 60%, rgb(255 255 255 / 1) 65%, rgb(255 255 255 / 0) 65%, rgb(255 255 255 / 0) 70%, rgb(255 255 255 / 1) 70%, rgb(255 255 255 / 1) 75%, rgb(255 255 255 / 0) 75%, rgb(255 255 255 / 0) 80%, rgb(255 255 255 / 1) 80%, rgb(255 255 255 / 1) 85%, rgb(255 255 255 / 0) 85%, rgb(255 255 255 / 0) 90%, rgb(255 255 255 / 1) 90%, rgb(255 255 255 / 1) 95%, rgb(255 255 255 / 0) 95%);  
    }
    <div class="full"></div>
    <div class="left"></div>
    <div class="right"></div>
    <div class="c1"></div>
    <div class="c2">
      <div class="striped">X</div>
    </div>

    After running this snippet, use the full page link to test the responsive behaviour.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search