skip to Main Content

So I have a very simple set of divs in a container.

<div class="container">
 <div id="box1" class="corner"></div>
 <div id="box2" class="corner right"></div>
 <div id="box3" class="corner"></div>
 <div id="boxAlways" class="corner right"></div>
</div>

I currently have some CSS that makes these boxes only take up half the width of the container so they display in a 2×2 grid (it works like this, but I’m unsure if I’ve got the display elements wrong):

.container {
 display: block;
}

.corner {
 width: 50%;
 display: inline-block;
 position: relative;
}

.right {
 float: right
}

Finally, I have some JS that hides any boxes if they contain no text. There are some rules:

  • BoxAlways ALWAYS contains text and will never disappear
  • Other boxes will not have text starting from the last box, so only the following circumstances can occur:
  • all three boxes can be empty
  • box1 can contain text whilst box2 and 3 can be empty
  • box1 and 2 can contain text whilst box3 can be empty
  • all three boxes can contain text

Here’s what I want to do with the CSS, which I can’t work out. If there is an odd number of boxes, I want the box that’s on its own to fill 100% of the width instead of just 50%. I believe this requires a full rewrite of the CSS rather than just a tweak, but I can’t get my head around how this could work, but knowing how clever CSS is, I’m sure it can be done.

It must be done without fixed sizes, as this CSS will affect multiple HTML pages. If it can’t be done in the CSS, I’m sure I can do something with the JS, but didn’t want to go down that route and potentially miss a simple solution. Thanks all!

3

Answers


  1. To achieve the layout where a single box fills 100% width when there’s an odd number of boxes, you can adjust your CSS approach. Instead of relying solely on display: inline-block, which doesn’t handle the odd/even scenario gracefully, you can use Flexbox which provides better control over layout and alignment. Here’s how you can modify your CSS:

    .container {
      display: flex;
      flex-wrap: wrap;
    }
    
    .corner {
      width: 50%;
      box-sizing: border-box; /* Ensures padding and border are included in the width */
      padding: 10px; /* Adjust padding as needed */
    }
    
    .right {
      margin-left: auto; /* Pushes the element to the right */
    }
    
    /* Media query to handle smaller screens */
    @media (max-width: 600px) {
      .corner {
        width: 100%; /* On smaller screens, each box takes full width */
      }
    }

    Explanation:

    1. Flex Container Setup: display: flex; on .container makes it a flex
      container, allowing flex items (.corner) to adjust their widths
      based on available space.

    2. Flex Wrap: flex-wrap: wrap; allows items to wrap to the next line if there isn’t enough space horizontally.

    3. Flex Item Properties:

    .corner initially set to width: 50%; ensures two items per row.
    box-sizing: border-box; includes padding and border in the element’s total width.
    .right uses margin-left: auto; to push items to the right within their flex container.

    1. Media Query: Added to handle smaller screens (max-width: 600px), where each box (corner) will take width: 100%; to ensure they stack vertically.

    Implementation Notes:
    Adjust .corner styles (like padding, margins, etc.) as per your design needs. This approach avoids JavaScript for layout adjustments, leveraging CSS’s flexbox for responsive and flexible layouts.

    By using Flexbox, you achieve a dynamic layout that adapts to different numbers of boxes without relying on fixed widths, meeting your requirement for a flexible and responsive design.

    Login or Signup to reply.
  2. CSS Grid can manage that. [Sizes are for demo purposes]

    .container {
      display: inline-grid;
      grid-template-columns: repeat(2, 75px);
      grid-template-rows: repeat (2, 75px));
      gap: .25em;
      border: 2px solid red;
      margin-bottom: .25em;
    }
    
    .corner {
      background: lightblue;
      height: 75px;
    }
    
    .hide {
      display: none;
    }
    
    #boxAlways {
      grid-column: 2;
      grid-row: 2;
      background: lightgreen;
    }
    
    #box1:nth-child(1):has(+.hide),
    .hide:nth-child(3)+#boxAlways {
      grid-column: 1 / span 2;
    }
    <div class="container">
      <div id="box1" class="corner">1</div>
      <div id="box2" class="corner right hide">2</div>
      <div id="box3" class="corner hide">3</div>
      <div id="boxAlways" class="corner right">sometext</div>
    </div>
    
    <div class="container">
      <div id="box1" class="corner">1</div>
      <div id="box2" class="corner right">2</div>
      <div id="box3" class="corner hide">3</div>
      <div id="boxAlways" class="corner right">sometext</div>
    </div>
    
    <div class="container">
      <div id="box1" class="corner">1</div>
      <div id="box2" class="corner right">2</div>
      <div id="box3" class="corner">3</div>
      <div id="boxAlways" class="corner right">sometext</div>
    </div>
    Login or Signup to reply.
  3. Since you’ve only got four boxes, the simplest thing is to just enumerate the possibilities and write the css for each. There’s only four cases and of those, and assuming you intend for box1 and boxAlways to share a row when box2 and box3 are empty, only two cases require a box to have 100% width, and in each case it’s only boxAlways that can have that 100% width.

    The first case is when only box3 is empty and the other is when box1, box2, and box3 are empty. So if you’ve marked the empty boxes with a "hide" class, you can describe the selector like this:

      #boxAlways {
        .hide + .hide + .hide + &,
        :not(.hide) + :not(.hide) + .hide + & {
          width: 100%;
        }
      } 
    
    @layer normal, priority;
    @layer priority {
      .hide {
        display:none;
      }
    }
    
    @layer normal {
      .container {
        font-size: 0; 
        border: 1px solid;
        margin-block-end: 20px;
      }
    
      .corner {
        font-size: 1rem;
        width: 50%;
        display: inline-block;
        text-align: center;
        background-color: moccasin;
      }
      
      #boxAlways {
        background-color: lightblue;
    
        .hide + .hide + .hide + &,
        :not(.hide) + :not(.hide) + .hide + & {
          width: 100%;
        }
      }
    }
    <body>
      <div class="container">
      <div id="box1" class="corner hide">1</div>
      <div id="box2" class="corner right hide">2</div>
      <div id="box3" class="corner hide">3</div>
      <div id="boxAlways" class="corner right">box always</div>
    </div>
    <div class="container">
      <div id="box1" class="corner">1</div>
      <div id="box2" class="corner right hide">2</div>
      <div id="box3" class="corner hide">3</div>
      <div id="boxAlways" class="corner right">box always</div>
    </div>
    
    <div class="container">
      <div id="box1" class="corner">1</div>
      <div id="box2" class="corner right">2</div>
      <div id="box3" class="corner hide">3</div>
      <div id="boxAlways" class="corner right">box always</div>
    </div>
    
    <div class="container">
      <div id="box1" class="corner">1</div>
      <div id="box2" class="corner right">2</div>
      <div id="box3" class="corner">3</div>
      <div id="boxAlways" class="corner right">box always</div>
    </div>

    Or, if you just want to measure emptyness, use the :empty pseudo-class

    @layer normal, priority;
    @layer priority {
      :empty {
        display:none;
      }
    }
    
    @layer normal {
      .container {
        font-size: 0; 
        border: 1px solid;
        margin-block-end: 20px;
      }
    
      .corner {
        font-size: 1rem;
        width: 50%;
        display: inline-block;
        text-align: center;
        background-color: moccasin;
      }
      
      #boxAlways {
        background-color: lightblue;
    
        :empty + :empty + :empty + &,
        :not(:empty) + :not(:empty) + :empty + & {
          width: 100%;
        }
      }
    }
    <div class="container">
      <div id="box1" class="corner"></div>
      <div id="box2" class="corner"></div>
      <div id="box3" class="corner"></div>
      <div id="boxAlways" class="corner">box always</div>
    </div>
    <div class="container">
      <div id="box1" class="corner">1</div>
      <div id="box2" class="corner"></div>
      <div id="box3" class="corner"></div>
      <div id="boxAlways" class="corner">box always</div>
    </div>
    
    <div class="container">
      <div id="box1" class="corner">1</div>
      <div id="box2" class="corner">2</div>
      <div id="box3" class="corner"></div>
      <div id="boxAlways" class="corner">box always</div>
    </div>
    
    <div class="container">
      <div id="box1" class="corner">1</div>
      <div id="box2" class="corner">2</div>
      <div id="box3" class="corner">3</div>
      <div id="boxAlways" class="corner">box always</div>
    </div>

    On the other hand, if you intend that box1 and boxAlways should remain on different rows and each of them should take up 100%, then just extend the css of the cases sightly:

      #boxAlways {
        :empty + :empty + :empty + &,
        :not(:empty) + :empty + :empty + &,
        :not(:empty) + :not(:empty) + :empty + & {
          width: 100%;
        }
      }
      
      #box1:has(+ :empty + :empty) {
        width: 100%;
      }
    
    @layer normal, priority;
    @layer priority {
      :empty {
        display:none;
      }
    }
    
    @layer normal {
      .container {
        font-size: 0; 
        border: 1px solid;
        margin-block-end: 20px;
      }
    
      .corner {
        font-size: 1rem;
        width: 50%;
        display: inline-block;
        text-align: center;
        background-color: moccasin;
      }
      
      #boxAlways {
        background-color: lightblue;
    
        :empty + :empty + :empty + &,
        :not(:empty) + :empty + :empty + &,
        :not(:empty) + :not(:empty) + :empty + & {
          width: 100%;
        }
      }
      
      #box1:has(+ :empty + :empty) {
        width: 100%;
      }
    }
    <div class="container">
      <div id="box1" class="corner"></div>
      <div id="box2" class="corner"></div>
      <div id="box3" class="corner"></div>
      <div id="boxAlways" class="corner">box always</div>
    </div>
    <div class="container">
      <div id="box1" class="corner">1</div>
      <div id="box2" class="corner"></div>
      <div id="box3" class="corner"></div>
      <div id="boxAlways" class="corner">box always</div>
    </div>
    
    <div class="container">
      <div id="box1" class="corner">1</div>
      <div id="box2" class="corner">2</div>
      <div id="box3" class="corner"></div>
      <div id="boxAlways" class="corner">box always</div>
    </div>
    
    <div class="container">
      <div id="box1" class="corner">1</div>
      <div id="box2" class="corner">2</div>
      <div id="box3" class="corner">3</div>
      <div id="boxAlways" class="corner">box always</div>
    </div>

    I have used `display:inline-block` because that’s what you used. You can of course use flexbox or grid as suggested in the other answers if you prefer, but neither are necessary to meet the requirements.

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