Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Specifying content of which cells expand cell areas and which do not in CSS Grid

Tags:

html

css

css-grid

I want to have a cell in a grid with unimportant information which doesn't make any other cells expand beyond what's absolutely required. Its height should be no more than the height of other cells and its width should be that of empty unoccupied area.

Let's say for each item I have a thumbnail, a title (single unwrappable line), info (zero or more lines), description (possibly very long, displayed only if there's empty space).

I have come up with this:

.list {
  display: flex;
  flex-wrap: wrap;
  font: 12px/14px Verdana;
}
.item {
  display: grid;
  grid-template-areas:
    "thumb thumb thumb thumb"
    "sl    title desc  sr   "
    "sl    info  desc  sr   ";
  grid-template-columns:
    auto auto 1fr auto;
  grid-template-rows:
    auto auto 1fr;
  min-width: calc(var(--min-item-size) + 4px);
  max-width: calc(var(--max-item-size) + 4px);
  border: dotted 2px darkblue;
  margin: 4px 2px auto 2px;
}
.item > * {
  min-width: 0;
  min-height: 0;
  border: dotted 1px dodgerblue;
  margin: 1px;
}
.thumb {
  grid-area: thumb;
  margin: auto;
  height: 100px;
  background: linear-gradient(lightskyblue, deepskyblue);
}
.title {
  grid-area: title;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.info {
  grid-area: info;
  overflow: hidden;
  text-overflow: ellipsis;
}
.desc {
  grid-area: desc;
  position: relative;
  overflow: hidden;
}
.desc-text::after {
  position: absolute; /*clean hack*/
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
  content: var(--lorem-ipsum);
}
.desc-text.short::after {
  content: var(--lorem-ipsum-short);
}
:root {
  --lorem-ipsum: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
  --lorem-ipsum-short: "Lorem";
  --min-item-size: 100px;
  --max-item-size: 150px;
}
<div class="list">
  <div class="item">
    <div class="thumb" style="width: 150px">1</div>
    <div class="title">Short title</div>
    <div class="info">Short info</div>
    <div class="desc">
      <div class="desc-text"></div>
    </div>
  </div>
  <div class="item">
    <div class="thumb" style="width: 150px">2</div>
    <div class="title">Very long and detailed title</div>
    <div class="info">Very long and detailed info</div>
    <div class="desc">
      <div class="desc-text"></div>
    </div>
  </div>
  <div class="item">
    <div class="thumb" style="width: 50px">3</div>
    <div class="title">Short title</div>
    <div class="info">Short info</div>
    <div class="desc">
      <div class="desc-text"></div>
    </div>
  </div>
  <div class="item">
    <div class="thumb" style="width: 50px">4</div>
    <div class="title">Very long and detailed title</div>
    <div class="info">Very long and detailed info</div>
    <div class="desc">
      <div class="desc-text"></div>
    </div>
  </div>
  <div class="item" style="grid-template-columns: 22px auto 1fr 22px /*dirty hack*/">
    <div class="thumb" style="width: 150px">5</div>
    <div class="title">Short title</div>
    <div class="info">Short info</div>
    <div class="desc">
      <div class="desc-text short"></div>
    </div>
  </div>
  <div class="item">
    <div class="thumb" style="width: 150px">6</div>
    <div class="title">Very long and detailed title</div>
    <div class="info">Very long and detailed info</div>
    <div class="desc">
      <div class="desc-text short"></div>
    </div>
  </div>
</div>

I had to put description into an absolutely positioned container inside relatively positioned cell. This makes the grid ignore contents and kinda works, but there're several issues:

  1. If description is short (see item #5), I can't center the information block, as 1fr is used for description, so setting spacing columns (sl and sr) to anything other than 0 doesn't work (dirty hack in the form of style attribute doesn't count, it's just to display the use of spacing cells).

  2. It doesn't look like a pure CSS Grid solution. I'd like to know whether there're more "native" ways of specifying how cell growing is affected by content.


If you want a "clean start", you can remove the position: absolute; /*clean hack*/ line from CSS and the style="grid-template-columns: 22px auto 1fr 22px /*dirty hack*/" attribute from HTML. Some positioning CSS properties become obsolete, but they stop affecting positioning. They can be removed too if you want.

Clean start:

.list {
  display: flex;
  flex-wrap: wrap;
  font: 12px/14px Verdana;
}
.item {
  display: grid;
  grid-template-areas:
    "thumb thumb thumb thumb"
    "sl    title desc  sr   "
    "sl    info  desc  sr   ";
  grid-template-columns:
    auto auto 1fr auto;
  grid-template-rows:
    auto auto 1fr;
  min-width: calc(var(--min-item-size) + 4px);
  max-width: calc(var(--max-item-size) + 4px);
  border: dotted 2px darkblue;
  margin: 4px 2px auto 2px;
}
.item > * {
  min-width: 0;
  min-height: 0;
  border: dotted 1px dodgerblue;
  margin: 1px;
}
.thumb {
  grid-area: thumb;
  margin: auto;
  height: 100px;
  background: linear-gradient(lightskyblue, deepskyblue);
}
.title {
  grid-area: title;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.info {
  grid-area: info;
  overflow: hidden;
  text-overflow: ellipsis;
}
.desc {
  grid-area: desc;
  overflow: hidden;
}
.desc-text::after {
  content: var(--lorem-ipsum);
}
.desc-text.short::after {
  content: var(--lorem-ipsum-short);
}
:root {
  --lorem-ipsum: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
  --lorem-ipsum-short: "Lorem";
  --min-item-size: 100px;
  --max-item-size: 150px;
}
<div class="list">
  <div class="item">
    <div class="thumb" style="width: 150px">1</div>
    <div class="title">Short title</div>
    <div class="info">Short info</div>
    <div class="desc">
      <div class="desc-text"></div>
    </div>
  </div>
  <div class="item">
    <div class="thumb" style="width: 150px">2</div>
    <div class="title">Very long and detailed title</div>
    <div class="info">Very long and detailed info</div>
    <div class="desc">
      <div class="desc-text"></div>
    </div>
  </div>
  <div class="item">
    <div class="thumb" style="width: 50px">3</div>
    <div class="title">Short title</div>
    <div class="info">Short info</div>
    <div class="desc">
      <div class="desc-text"></div>
    </div>
  </div>
  <div class="item">
    <div class="thumb" style="width: 50px">4</div>
    <div class="title">Very long and detailed title</div>
    <div class="info">Very long and detailed info</div>
    <div class="desc">
      <div class="desc-text"></div>
    </div>
  </div>
  <div class="item">
    <div class="thumb" style="width: 150px">5</div>
    <div class="title">Short title</div>
    <div class="info">Short info</div>
    <div class="desc">
      <div class="desc-text short"></div>
    </div>
  </div>
  <div class="item">
    <div class="thumb" style="width: 150px">6</div>
    <div class="title">Very long and detailed title</div>
    <div class="info">Very long and detailed info</div>
    <div class="desc">
      <div class="desc-text short"></div>
    </div>
  </div>
</div>
like image 209
Athari Avatar asked Jan 07 '18 11:01

Athari


2 Answers

I think that I have got a quite good solution.

The only drawback is that I need to set a max-width on .title and .info.

Your first problem is distributing the horizontal space. You want the second column to give as much space as possible to title. But not more than the natural width of the contained elements. So, we need something that is relative to content, and to the max width of it, max-content.

Then we want the third column, with desc, to take any left width if there is any, and also not more that what it can fill. This can be achieved with min-max(0, max-content)

And finally, we want any left space to go the the 1st and 4th columns, with 1fr.

This arrangement works because the free space is distributed in 2 rounds, one for minmax functiones and the second for fr elements.

The problem with this setup is that the max-content of title can overflow the container. We can not solve (I think) this on the grid itself, and we need to set a max-width on the element itself.

At first, you could think that a minmax function here could help. No, it can't. You can see an explanation here

The other problem is handling the height of desc. Since it spans 2 rows, but both have to be sensible to the content, we are out of luck here.

By luck, you have already a nested structure here. Making the container overflow: hidden and limiting the inner element height will do the trick. The inner element doesn't have enough height for its contents, but they are displayed because of overflow: none. And the container fits the height to the grid, and clips the overflowed content

:root {
  --lorem-ipsum: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
  --lorem-ipsum-short: "Lorem";
  --min-item-size: 100px;
  --max-item-size: 150px;
  --max-item-desc-height: 32px; /* added */
}

.list {
  display: flex;
  flex-wrap: wrap;
  font: 12px/14px Verdana;
}
.item {
  display: grid;
  grid-template-areas:
    "thumb thumb thumb thumb"
    "sl    title desc  sr   "
    "sl    info  desc  sr   ";
  grid-template-columns: 1fr max-content minmax(0px, max-content) 1fr; /* changed */
  grid-template-rows: auto auto 1fr;
  min-width: calc(var(--min-item-size) + 4px);
  max-width: calc(var(--max-item-size) + 4px);
  border: dotted 2px darkblue;
  margin: 4px 2px auto 2px;
}
.item > * {
  min-width: 0;
  min-height: 0;
  border: dotted 1px dodgerblue;
  margin: 1px;
}
.thumb {
  grid-area: thumb;
  margin: auto;
  height: 100px;
  background: linear-gradient(lightskyblue, deepskyblue);
}
.title {
  grid-area: title;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  max-width: var(--max-item-size); /* added */
}
.info {
  grid-area: info;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: var(--max-item-size); /* added */
}
.desc {
  grid-area: desc;
  overflow: hidden;
  max-height: var(--max-item-desc-height); /* added */
}
.desc-text::after {
  content: var(--lorem-ipsum);
}
.desc-text.short::after {
  content: var(--lorem-ipsum-short);
}
<div class="list">
  <div class="item">
    <div class="thumb" style="width: 150px">1</div>
    <div class="title">Short title</div>
    <div class="info">Short info</div>
    <div class="desc">
      <div class="desc-text"></div>
    </div>
  </div>
  <div class="item">
    <div class="thumb" style="width: 150px">2</div>
    <div class="title">Very long and detailed title</div>
    <div class="info">Very long and detailed info</div>
    <div class="desc">
      <div class="desc-text"></div>
    </div>
  </div>
  <div class="item">
    <div class="thumb" style="width: 50px">3</div>
    <div class="title">Short title</div>
    <div class="info">Short info</div>
    <div class="desc">
      <div class="desc-text"></div>
    </div>
  </div>
  <div class="item">
    <div class="thumb" style="width: 50px">4</div>
    <div class="title">Very long and detailed title</div>
    <div class="info">Very long and detailed info</div>
    <div class="desc">
      <div class="desc-text"></div>
    </div>
  </div>
  <div class="item">
    <div class="thumb" style="width: 150px">5</div>
    <div class="title">Short title</div>
    <div class="info">Short info</div>
    <div class="desc">
      <div class="desc-text short"></div>
    </div>
  </div>
  <div class="item">
    <div class="thumb" style="width: 150px">6</div>
    <div class="title">Very long and detailed title</div>
    <div class="info">Very long and detailed info</div>
    <div class="desc">
      <div class="desc-text short"></div>
    </div>
  </div>
</div>
like image 174
vals Avatar answered Nov 05 '22 05:11

vals


The question is somewhat difficult to understand and the example given convolutes things. So my apologies if Im not answering your question directly.

What I think you're asking (based on your example and it's requirements) is: "Can you prioritise which elements grow or shrink regardless of their content?"

The answer to that question is no, however that doesn't mean you can't achieve what you want while still only using CSS. Please see my below snippet on how I approached / solved the issue.

Please note: I think you've made things hard on yourself by trying to use the grid rules or even flex for that matter as I ran into the same issues you did when trying to solve this with flex.

.list {
  display: flex;
  flex-wrap: wrap;
  font: 12px/14px Verdana;
}

.item {
  min-width: 100px;
  max-width: 150px;
  border: solid 1px black;
  float: left;
  margin: 10px;
}

.thumb {
  margin: auto;
  height: 100px;
  background: linear-gradient(lightskyblue, deepskyblue);
}

.center {
  float: left;
  left: 50%;
  transform: translateX(-50%);
  position: relative;
  max-width: 150px;
}

.meta-wrap {
  float: left;
  max-width: 100%;
}

.desc-wrap {
  overflow: hidden;
  /*
  Make sure this doesn't grow out the rest of the item.
  This can be a static height because if the info is long enough to grow the height
  then the description wouldn't be seen anyway.
  */
  max-height: 27px;
}

.title,
.info,
.text {
  overflow: hidden;
  text-overflow: ellipsis;
}

.title {
  background: orange;
  white-space: nowrap;
}

.info {
  background: tomato;
}

.text {
  background: aqua;
}
<div class="list">

  <div class="item">
    <div class="thumb" style="width: 150px">1</div>
    <div class="center">
      <div class="meta-wrap">
        <div class="title">Short</div>
        <div class="info">Short</div>
      </div>
      <div class="desc-wrap">
        <div class="text">Lorem ipsum text and stuff goes here, cool!</div>
      </div>
    </div>
  </div>

  <div class="item">
    <div class="thumb" style="width: 150px">2</div>
    <div class="center">
      <div class="meta-wrap">
        <div class="title">Long title goes here that should cut off</div>
        <div class="info">long description goes here</div>
      </div>
      <div class="desc-wrap">
        <div class="text">Lorem ipsum text and stuff goes here, cool!</div>
      </div>
    </div>
  </div>

  <div class="item">
    <div class="thumb" style="width: 50px">3</div>
    <div class="center">
      <div class="meta-wrap">
        <div class="title">Short</div>
        <div class="info">Short</div>
      </div>
      <div class="desc-wrap">
        <div class="text">Lorem!</div>
      </div>
    </div>
  </div>

  <div class="item">
    <div class="thumb" style="width: 50px">4</div>
    <div class="center">
      <div class="meta-wrap">
        <div class="title">Long title goes here that should cut off</div>
        <div class="info">long description goes here</div>
      </div>
      <div class="desc-wrap">
        <div class="text">Lorem ipsum text and stuff goes here, cool!</div>
      </div>
    </div>
  </div>

  <div class="item">
    <div class="thumb" style="width: 150px">5</div>
    <div class="center">
      <div class="meta-wrap">
        <div class="title">Short</div>
        <div class="info">Short</div>
      </div>
      <div class="desc-wrap">
        <div class="text">Lorem!</div>
      </div>
    </div>
  </div>

  <div class="item">
    <div class="thumb" style="width: 150px">6</div>
    <div class="center">
      <div class="meta-wrap">
        <div class="title">Long title goes here that should cut off</div>
        <div class="info">long description goes here</div>
      </div>
      <div class="desc-wrap">
        <div class="text"></div>
      </div>
    </div>
  </div>

</div>
like image 23
Jesse Avatar answered Nov 05 '22 03:11

Jesse