Search code examples
htmlcssflexboxresponsive-designaspect-ratio

Empty responsive square with sides equal to sibling's height inside a flexbox


I have a flex container with two elements inside of it.

One is text inside of a div. Second is a box that should be a square.

  • Side of the square must be equal to the text's line-height (if text size changes, box should scale);
  • If text size changes, container's height should also change.
  • Box should stick to the right side of flex container (it does now).

What I have so far:

Current state

What I'm struggling to achieve:

Desired state

I have tried aspect-ratio CSS property, but it doesn't work without explicit height declaration. If I explicitly set height to 100%, box collapses vertically. Padding technique only for boxes with explicit width, thus not applicable. Thought about using font-size as height, but former not equal to line-height.

Looking for a CSS solution.

* {
  box-sizing: border-box;
  padding: 0;
  margin: 0;
  font-size: 22px;
}

.container {
  position: relative;
  left: 30%;
  top: 50px;
  width: 40%;
  border: 1px solid black;
  padding: 8px 24px;
  border-radius: 30px;
}

.task {
  display: flex;
  justify-content: space-between;
  border: 1px solid green;
}

.task-info {
  border: 1px solid blue;
}

.task-button {
  border: 1px solid red;
}
<div class="container">
  <div class="task">
    <div class="task-info">
      Content
    </div>
    <div class="task-button">
    </div>
  </div>
</div>


Solution

  • If you just need a square on the right then this can be drawn as a pseudo element:

    * {
      box-sizing: border-box;
      padding: 0;
      margin: 0;
      font-size: 22px;
    }
    
    .container {
      position: relative;
      left: 30%;
      top: 50px;
      width: 40%;
      border: 1px solid black;
      padding: 8px 24px;
      border-radius: 30px;
    }
    
    .task {
      display: flex;
      justify-content: space-between;
      border: 1px solid green;
      position: relative;
    }
    
    .task-info {
      border: 1px solid blue;
    }
    
    .task-info::after {
      content: '';
      position: absolute;
      top: 0;
      right: 0;
      border: 1px solid red;
      height: 100%;
      aspect-ratio: 1;
      z-index: -1;
    }
    
    .task-button {
      border: 1px solid transparent;
    }
    <div class="container">
      <div class="task">
        <div class="task-info">
          Content
        </div>
      </div>
    </div>

    However, I guess you may want more than this - e.g. clickable. One way to achieve this would be to have the clicking on the whole content element - but it depends on exactly what is required functionally.