Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get all items to shrink to the largest item content two dimensionally?

In the example below I desire to get all items to be the width of largest content (e.g item 5).

  • The number of items in the list is dynamic and there could be hundred of them.

  • The width of each item content is also dynamic.

  • The number of items in each row would vary dependant on the width of the container.

DESIRED OUTPUT

enter image description here

Note: Media queries won't work for my scenario.


USING CSS FLEX

Flex is considered to be one dimensional. This means it can achieve the desired result on a single axis (row) unless the content is smaller than the available space.

Non wrapping example (Single row)

.container {
  display: flex;
}

.item {
  background: #58c;
  text-align: center;
  white-space: nowrap;
  margin: 0 10px 10px 0;
  flex: 1;
}
<div class="container">
  <div class="item">1</div>
  <div class="item">2</div>
  <div class="item">3</div>
  <div class="item">4</div>
  <div class="item">5 is longer</div>
  <div class="item">6</div>
  <div class="item">7</div>
  <div class="item">8</div>
</div>

Flex's flex-wrap attribute can be used to create a two dimensional layout. Again this works nice but requires a fixed width value in order to calculate each items width.

Wrapping example (Multiple rows)

.container {
  display: flex;
  flex-wrap:wrap;
}

.item {
  background: #58c;
  text-align: center;
  white-space: nowrap;
  margin: 0 10px 10px 0;
  flex: 0 1 100px;
}
<div class="container">
  <div class="item">1</div>
  <div class="item">2</div>
  <div class="item">3</div>
  <div class="item">4</div>
  <div class="item">5 is longer</div>
  <div class="item">6</div>
  <div class="item">7</div>
  <div class="item">8</div>
  <div class="item">9</div>
  <div class="item">10</div>
</div>

USING CSS GRID

For two dimensional design, CSS Grid is recommended but the example below is using a fixed width. It seems like min-content needs factoring in here, but how?

Failed example

grid-template-columns: repeat(auto-fit, min-content);

FIXED EXAMPLE (Multple rows)

.container {
  display: grid;
  grid-template-columns: repeat(auto-fit, 100px);
  grid-auto-rows: 20px;
  grid-gap: 5px;
}

.item {
  background: #58c;
  text-align: center;
  white-space: nowrap;
}
<div class="container">
  <div class="item">1</div>
  <div class="item">2</div>
  <div class="item">3</div>
  <div class="item">4</div>
  <div class="item">5 is longer</div>
  <div class="item">6</div>
  <div class="item">7</div>
  <div class="item">8</div>
  <div class="item">9</div>
  <div class="item">10</div>
</div>

I do not think this is achievable with grid because of the following W3C note:

"Automatic repetitions (auto-fill or auto-fit) cannot be combined with intrinsic or flexible sizes."


Summary

Fundamentally I think there is no intrinsic way to do this with css.

To get the width of each item it needs to be:

  • predefined (fixed).
  • inferred horizontally using a percentage or fraction.
  • inferred vertically from the current column.

I see no way to infer an items width from another row AND column simultaneously.

like image 518
DreamTeK Avatar asked Apr 30 '18 09:04

DreamTeK


People also ask

How do I make a div the same size as a content?

You can simply use the CSS display property with the value inline-block to make a <div> not larger than its contents (i.e. only expand to as wide as its contents).


1 Answers

This seems like a problem that CSS Grid should be able to handle. But I haven't seen anything in the spec that provides a solution. I'm not saying it's not there, either as one simple command or a combination of rules. I just haven't found it (if it exists).

Here's the closest I've been able to get:

.container {
  display: grid;
  grid-auto-columns: min-content; /* max-content works, as well */
  grid-gap: 10px;
}

.item {
  background: #58c;
  text-align: center;
  white-space: nowrap;
}
<div class="container">
  <div class="item">1</div>
  <div class="item">2</div>
  <div class="item">3</div>
  <div class="item">4</div>
  <div class="item">5 is longer</div>
  <div class="item">6</div>
  <div class="item">7</div>
  <div class="item">8</div>
  <div class="item">9</div>
  <div class="item">10</div>
</div>

The code above solves the width problem — all items are the length of the longest item. But there's no wrapping. The problem is flipped in the example below.

.container {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min-content, 1px));
  grid-gap: 10px;
}

.item {
  background: #58c;
  text-align: center;
  white-space: nowrap;
}
<div class="container">
  <div class="item">1</div>
  <div class="item">2</div>
  <div class="item">3</div>
  <div class="item">4</div>
  <div class="item">5 is longer</div>
  <div class="item">6</div>
  <div class="item">7</div>
  <div class="item">8</div>
  <div class="item">9</div>
  <div class="item">10</div>
</div>

The code above gets the wrapping going, and gets around the intrinsic size limitation when using repeat(). However, the longest-content equal length requirement fails.

7.2.2.1. Syntax of repeat()

Automatic repetitions (auto-fill or auto-fit) cannot be combined with intrinsic or flexible sizes.

  • An intrinsic sizing function is min-content, max-content, auto, fit-content().

  • A flexible sizing function is <flex> (fr).

You can get around the auto limitation with something like this:

repeat(auto-fill, minmax(min-content, 1px))

minmax(min,max)

Defines a size range greater than or equal to min and less than or equal to max.

If max < min, then max is ignored and minmax(min,max) is treated as min.

As a maximum, a <flex> value sets the track’s flex factor; it is invalid as a minimum.

Maybe somebody else can take it from here.

like image 132
Michael Benjamin Avatar answered Sep 20 '22 19:09

Michael Benjamin