1

I have two side-by-side grid columns. I want the grid to be the width of its container. Within that space, I want to ensure that it will always be the case that as much of each column's content is as visible as possible.

I made this demo where the grid is 300px wide, and the two columns are content editable so you can type to change the content length (and thus the values of min-content and max-content will also change as you type).

I am using grid-template-columns: minmax(min-content, 1fr) minmax(min-content, 1fr); which is my best attempt yet to achieve what I want, but still isn't sufficient.

html, body {
  width: 300px;
}

html * {
  border: 1px solid black;
}

span {
  cursor: pointer;
}

body {
  display: grid;
  grid-template-columns: minmax(min-content, 1fr) minmax(min-content, 1fr);
}
<span contenteditable></span>
<span contenteditable></span>

When there is nothing in the columns, the grid is balanced 50/50. Each column is 150px wide. This is good.

When there is more than 150px of content in one column, it becomes wider and begins to expand into the space the other column had available to itself. This is also good.

But: when the sum of the two columns' contents is greater than 300px, the grid starts overflowing horizontally:

enter image description here

Instead I would like the grid's width to be constrained to 300px, and the widest cell should shrink itself until as much as possible of the smaller cell's content is visible:

enter image description here

And the cell's overflow-x: ellipsis is applied.

This should happen in the other direction too. Current and undesirable behaviour:

enter image description here

Desired behaviour:

enter image description here

When each cell on its own is already wider than half the grid, 150px in this case (current undesired behaviour):

enter image description here

They should both be shrunk until proportionally balanced (desired behaviour):

enter image description here

With a final (optional) goal of having constrained widths which are representative of their actual content widths:

enter image description here enter image description here


I am surprised I haven't yet been able to find a standard solution for balancing side-by-side grid columns, it seems like it would be a common use case. It's very similar to this: Is it possible to set proportional grid columns based on content?, except I don't know the proportions ahead of time and they're co-dependent. I would really appreciate being pointed towards anything I may have missed.

To try to achieve this, I have tried a variety of grid-template-columns values already, but there seems to be a CSS-expressivity problem that the widths of columns cannot depend on the contents of the other columns. Unfortunately calc(min-content/100%) seems to invalidate the property value. I may just be missing a way to accomplish it with pure CSS, I would be over the moon if there were a CSS-only answer.

I have been trying with JavaScript since but because min-content and max-content cannot be discovered easily using JavaScript I thought I would ask here before committing several more hours to pursuing the solution I have thought of for now: allowing the grid width to be totally unlimited, measuring the outerWidth of each cell, then setting grid-template-columns as the outerWidth / sumOfBothOuterWidth percentage for each cell. All of this would have to be put into some MutationObserver callback too, so that it can re-calculate widths when content changes. Luckily because of using percentage values instead of hardcoded ones, resizing the window/container should redraw automatically.

Could anyone recommend an alternative solution?

The best answer would be CSS-only, and I will generally prefer solutions that require less JavaScript DOM manipulation and JavaScript calculation.

theonlygusti
  • 11,032
  • 11
  • 64
  • 119

2 Answers2

2

This is a case where I think flexbox is the perfect solution to use in conjunction with grid. You can place a flex element within a grid row and it will easily handle the grow and shrink features you are looking for with minimal css and no JS.

In my example below the column with the most content will grow larger without overflowing into the next column while the other column shrinks proportionally. The child elements of the flex container are set to grow and shrink equally (flex: 1 1) or occupy 150px (flex: 1 1 150) of available space if the content is less than the available width. The flex-basis is set to 150 which is half of the width you specified. For larger widths adjust the flex-basis accordingly.

*, ::before, ::after {
    box-sizing: border-box;
}

body {
    width: 300px;
}
    
html * {
    border: 1px solid black;
}

.flex-col {
    display: flex;
    flex-wrap: nowrap;
    gap: 1rem;
    
}

.flex-col > * {
    flex: 1 1 150;
}

.col {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}
<div class="flex-col">
        <div class="col" contenteditable>Lorem ipsum dolor sit amet consectetur adipisicing elit. </div>
        <div class="col" contenteditable>
            Alias, soluta Lorem ipsum dolor sit, amet consectetur adipisicing elit. Veritatis natus odio dolorum. Dolorem minus, labore distinctio eum praesentium eos ullam ducimus repellat, rerum vel eveniet soluta laborum odit odio sequi. </div>
</div>
theonlygusti
  • 11,032
  • 11
  • 64
  • 119
Manny
  • 94
  • 4
  • Great suggestion to use flex, thank you. When the divs in your solution have less than 150px of content, they become that narrow, instead of being the same size as 50% of the grid width. Do you know how to fix this? – theonlygusti Sep 10 '21 at 07:11
  • You said "flex in conjuction with grid" but I don't see grid in your answer. – theonlygusti Sep 10 '21 at 07:17
  • To prevent them shrinking below 150px set a min-width on the child of 150px. If you want it in a grid, wrap it with another div and declare it as a grid row. Do you need an example? – Manny Sep 10 '21 at 13:49
  • I would like them to be able to be smaller than 150px, if the other cell needs the space, but if they both have the same content width and that content width is 150px, they should both be 50% as wide as the container, which is 150px in this case – theonlygusti Sep 10 '21 at 15:36
1

It sounds like you are looking for a two column, fixed width grid where column widths are proportional to the text content they contain (and overflowing text content is truncated). I think the below gets you closer, but you will run into some issues once the combined text content exceeds the width of the grid. The grid columns will not continue to resize proportionally once the combined text content exceeds the width of the grid. Also, if you actually plan to use contenteditable elements in your grid, the combination of overflow: hidden and text-overflow: ellipsis will cause some browsers to stop displaying your input once the text begins to overflow (which means that if you enter enough characters, the column will eventually appear empty).

.grid {
  display: grid;
  grid-template-columns: repeat(2, auto);
  width: 300px;
}

.column {
  border: 1px solid gray;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
<div class="grid">
  <div class="column" contenteditable="true">
    abcdefghijklmnop
  </div>
  <div class="column" contenteditable="true">
    abcdefghijklmnopqrstuvwxyz
  </div>
</div>
benvc
  • 14,448
  • 4
  • 33
  • 54
  • This behaviour looks perfect until — as you pointed out too — the sum of the contents is wider than the grid. Do you think this solution has potential to deal with proportional sizes for wider-than-the-grid columns? – theonlygusti Sep 10 '21 at 08:29
  • @theonlygusti - not sure of a way to handle that part of what you are trying to accomplish without js. If you aren't worried about differing fonts, font sizes, styles, and character widths, you could just use js to compare column string lengths and set proportional widths (but it would be significantly more complicated to get a truly accurate proportion). – benvc Sep 10 '21 at 12:37
  • Do you think there's a way to make this work in a similar way for `` too? I made a new question for it: https://stackoverflow.com/q/69150051/3310334 It might be much harder because it looks like `` don't have any internal "display width" property that is based on their value – theonlygusti Sep 12 '21 at 09:29
  • @theonlygusti - definitely harder with input elements but looks like your other question found a nice answer – benvc Sep 13 '21 at 15:14