Asked  7 Months ago    Answers:  5   Viewed   40 times

I would like to use percentage for my css table. Unfortunately, it doesn't work for me.

What's wrong with this code? Should I use flexbox instead of table?

I would like to use table, because I would like same height columns.

ul {
  list-style: none;
  margin: 0;
  display: table;
  table-layout: fixed;
  width: 100%;
}

li {
  width: 50%;
  display: table-cell;
}
<ul>
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
  <li>5</li>
  <li>6</li>
</ul>

 Answers

43

Here is a sample using ul/li elements, 2 columns using percentage and have equal height.

As tables prefer table/row/cell layout, I restructured your html a little.

* {
    margin: 0;
}
html, body {
    height: 100%;
}
.table {
  display: table;
  width: 90%;
  height: 60%;
  padding-top: 5%;
  margin: 0 auto;
}
ul {
  display: table-row;
  list-style: none;
  margin: 0;
}

li {
  display: table-cell;
  width: 50%;
  border: 1px solid #999;
}
<div class="table">
  <ul>
    <li>1</li>
    <li>2</li>
  </ul>
  <ul>
    <li>3</li>
    <li>4</li>
  </ul>
  <ul>
    <li>5</li>
    <li>6</li>
  </ul>
</div>
Tuesday, June 1, 2021
 
aWebDeveloper
answered 7 Months ago
37

With CSS grid you can consider the use of max(width,100%/N) where N is the maximum number of columns. If the width of container increase, 100%/N will for sure be bigger than width thus we won't have more than N elements per row.

.grid-container {
  --n:4; /* the max number of columns */
  display: grid;
  grid-template-columns:repeat(auto-fill,minmax(max(200px,100%/var(--n)),1fr));
}

.grid-item {
  background: tomato;
  padding: 5px;
  height: 50px;
  margin: 10px;
  
  line-height: 50px;
  color: white;
  font-weight: bold;
  font-size: 2em;
  text-align: center;
  box-sizing:border-box;
}
<div class="grid-container">
  <div class="grid-item">1</div>
  <div class="grid-item">2</div>
  <div class="grid-item">3</div>
  <div class="grid-item">4</div>
  <div class="grid-item">5</div>
  <div class="grid-item">6</div>
  <div class="grid-item">7</div>
  <div class="grid-item">8</div>
</div>

<div class="grid-container" style="--n:3">
  <div class="grid-item">1</div>
  <div class="grid-item">2</div>
  <div class="grid-item">3</div>
  <div class="grid-item">4</div>
  <div class="grid-item">5</div>
  <div class="grid-item">6</div>
  <div class="grid-item">7</div>
  <div class="grid-item">8</div>
</div>

With gaps:

.grid-container {
  --n:4; /* the max number of columns */
  display: grid;
  grid-template-columns:repeat(auto-fill,
           minmax(max(200px,(100% - (var(--n) - 1)*10px)/var(--n)),1fr));
  gap:10px;
  margin:5px;
}

.grid-item {
  background: tomato;
  padding: 5px;
  height: 50px;
  
  line-height: 50px;
  color: white;
  font-weight: bold;
  font-size: 2em;
  text-align: center;
  box-sizing:border-box;
}
<div class="grid-container">
  <div class="grid-item">1</div>
  <div class="grid-item">2</div>
  <div class="grid-item">3</div>
  <div class="grid-item">4</div>
  <div class="grid-item">5</div>
  <div class="grid-item">6</div>
  <div class="grid-item">7</div>
  <div class="grid-item">8</div>
</div>

<div class="grid-container" style="--n:3">
  <div class="grid-item">1</div>
  <div class="grid-item">2</div>
  <div class="grid-item">3</div>
  <div class="grid-item">4</div>
  <div class="grid-item">5</div>
  <div class="grid-item">6</div>
  <div class="grid-item">7</div>
  <div class="grid-item">8</div>
</div>

With flexbox, you can simply set a max-width to the container since your elements have a fixed width:

.flex-container {
  display: flex;
  flex-wrap: wrap;
  max-width:calc(5*(200px + 20px)); 
}

.flex-item {
  background: tomato;
  padding: 5px;
  width: 200px;
  height: 100px;
  margin: 10px;
  
  line-height: 100px;
  color: white;
  font-weight: bold;
  font-size: 2em;
  text-align: center;
  box-sizing:border-box;
}
<div class="flex-container wrap">
  <div class="flex-item">1</div>
  <div class="flex-item">2</div>
  <div class="flex-item">3</div>
  <div class="flex-item">4</div>
  <div class="flex-item">5</div>
  <div class="flex-item">6</div>
  <div class="flex-item">7</div>
  <div class="flex-item">8</div>
</div>

The only drawback is that you need to know the width of your elements and their margin to correctly set the max-width.

If you want your elements to expand and cover all the width, you can use a trick with min-width like below:

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

.flex-item {
  background: tomato;
  padding: 5px;
  min-width: 200px;
  width:calc(100%/5 - 20px); /*5 columns*/
  height: 100px;
  margin: 10px;
  
  line-height: 100px;
  color: white;
  font-weight: bold;
  font-size: 2em;
  text-align: center;
  box-sizing:border-box;
}
<div class="flex-container wrap">
  <div class="flex-item">1</div>
  <div class="flex-item">2</div>
  <div class="flex-item">3</div>
  <div class="flex-item">4</div>
  <div class="flex-item">5</div>
  <div class="flex-item">6</div>
  <div class="flex-item">7</div>
  <div class="flex-item">8</div>
</div>

Here also you need to consider the margin, You can easily make this more flexible using CSS variable:

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

.flex-item {
  --m:10px;
  background: tomato;
  padding: 5px;
  min-width: 200px;
  width:calc(100%/5 - 2*var(--m)); /*5 columns*/
  height: 100px;
  margin: var(--m);
  
  line-height: 100px;
  color: white;
  font-weight: bold;
  font-size: 2em;
  text-align: center;
  box-sizing:border-box;
}
<div class="flex-container wrap">
  <div class="flex-item">1</div>
  <div class="flex-item">2</div>
  <div class="flex-item">3</div>
  <div class="flex-item">4</div>
  <div class="flex-item">5</div>
  <div class="flex-item">6</div>
  <div class="flex-item">7</div>
  <div class="flex-item">8</div>
</div>

You can also consider flex-grow if you want your element to always expand (even when there is a wrap) but you may face the issue of the last row that you need to fix with some hacks:

.flex-container {
  display: flex;
  flex-wrap: wrap;
  --m:10px;
}

.flex-item {
  background: tomato;
  padding: 5px;
  min-width: 200px;
  flex-grow:1;
  width:calc(100%/5 - 2*var(--m)); /*5 columns*/
  height: 100px;
  margin: var(--m);
  
  line-height: 100px;
  color: white;
  font-weight: bold;
  font-size: 2em;
  text-align: center;
  box-sizing:border-box;
}

.flex-container span {
  min-width: 200px;
  flex-grow:1;
  width:calc(100%/5 - 2*var(--m)); /*5 columns*/
  margin:0 var(--m);
}
<div class="flex-container wrap">
  <div class="flex-item">1</div>
  <div class="flex-item">2</div>
  <div class="flex-item">3</div>
  <div class="flex-item">4</div>
  <div class="flex-item">5</div>
  <div class="flex-item">6</div>
  <div class="flex-item">7</div>
  <div class="flex-item">8</div>
  
  <!-- 4 empty elements to fix the issue (we can also use pseudo element) -->
  <span></span>
  <span></span>
  <span></span>
  <span></span>
</div>

In the example below we made the number of columns to be 5 so we will need at least 4 empty element to fix the issue in case we will have one to 4 elements in the last row. Of course, this is a drawback but since you know the number of columns you can easily set those empty elements and you won't need any JS.

To make it more flexible, here is an idea with CSS variables:

.flex-container {
  display: flex;
  flex-wrap: wrap;
  border:1px solid;
  --m:10px;
  --n:5;
  --width:150px;
}

.flex-item {
  background: tomato;
  min-width: var(--width);
  flex-grow:1;
  width:calc(100%/var(--n) - 2*var(--m));
  height: 50px;
  margin: var(--m);
  
  box-sizing:border-box;
}

.flex-container span {
  display:contents; /* each span will give us 2 elements*/
}
.flex-container span:before,
.flex-container span:after,
.flex-container:before,
.flex-container:after{
  content:"";
  min-width: var(--width);
  flex-grow:1;
  width:calc(100%/var(--n) - 2*var(--m));
  margin:0 var(--m);
  order:1; /*we make sure they are at the end*/
}
<div class="flex-container wrap">
  <div class="flex-item">1</div>
  <div class="flex-item">2</div>
  <div class="flex-item">3</div>
  <div class="flex-item">4</div>
  <div class="flex-item">5</div>
  <div class="flex-item">6</div>
  <div class="flex-item">7</div>
  <div class="flex-item">8</div>
  
  <!-- a lot of elements !! -->
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
</div>

<div class="flex-container wrap" style="--n:10">
  <div class="flex-item">1</div>
  <div class="flex-item">2</div>
  <div class="flex-item">3</div>
  <div class="flex-item">4</div>
  <div class="flex-item">5</div>
  <div class="flex-item">6</div>
  <div class="flex-item">7</div>
  <div class="flex-item">8</div>
  
  <!-- a lot of elements !! -->
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
</div>

<div class="flex-container wrap" style="--n:3">
  <div class="flex-item">1</div>
  <div class="flex-item">2</div>
  <div class="flex-item">3</div>
  <div class="flex-item">4</div>
  <div class="flex-item">5</div>
  <div class="flex-item">6</div>
  <div class="flex-item">7</div>
  <div class="flex-item">8</div>
  
  <!-- a lot of elements !! -->
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
</div>

I used display:contents to be able to set N empty elements that will later be considered as 2*N which can reduce the code.

If you will have 7 columns, we will only need 6 extra elements. We can use the two pseudo elements then only 2 empty element to cover the remaining 4.

Wednesday, June 2, 2021
 
Evernoob
answered 7 Months ago
39

The key to this layout is to apply equal heights to the primary flex container.

Then make the flex items nested flex containers, which can center the content of the flex items.

Hence, the top level creates the equal height. The second level does the centering.

(See the note at the bottom for more details.)

Here's an example based on your code structure:

body {
    height: 300px;             /* for demo purposes */
    color: white;
}

flex-container {
    display: flex;             /* primary flex container */
    flex-direction: row;       /* horizontal alignment of flex items
                                      (default value; can be omitted) */
    align-items: stretch;      /* will apply equal heights to flex items
                                      (default value; can be omitted) */
    height: 100%;
}

flex-item {
    display: flex;             /* nested flex container */
    flex-direction: column;    /* vertical alignment of flex items */
    justify-content: center;   /* center flex items vertically */
    align-items: center;       /* center flex items horizontally */
}

flex-item:first-child {
    flex: 3;                   /* consume 3x more free space than sibling */
    background-color: #a333c8;
}

flex-item:last-child {
    flex: 1;
    background-color: #db2828;
}
<flex-container>
    <flex-item><!-- also flex container -->
        <p>Text Text Text</p>
        <p>Text Text Text</p>
        <p>Text Text Text</p>
        <p>Text Text Text</p>
    </flex-item>
    <flex-item><!-- also flex container -->
        <div>Forward</div>
    </flex-item>
</flex-container>

jsFiddle demo


People sometimes consider a flex item and its content to be one element. This is not correct.

The HTML structure of a flex container has three levels:

  • the container
  • the item
  • the content

Therefore, the content inside an item does not represent the item; it represents a separate element.

If the content inside an item is text, it becomes an anonymous element.

From the flexbox spec:

4. Flex Items

Each in-flow child of a flex container becomes a flex item, and each contiguous run of text that is directly contained inside a flex container is wrapped in an anonymous flex item.

That's why, in the solution above, the flex item becomes a flex container. It enables the use of flex properties on the children of the flex item (the content).

Wednesday, July 21, 2021
 
aurelijusv
answered 5 Months ago
74

Instead of flex: auto on grid, use flex: 1 on it so that the grid, and you have the desired behavior in both Firefox and Chrome. See demo below:

html,
body {
  height: 100%;
  padding: 0;
  margin: 0;
}

* { box-sizing: border-box; }

.container {
  height: 100%;
  display: flex;
  flex-direction: column;
  border: 2px solid red;
}

.header {
  background: lime;
}

.grid {
  flex: 1; /* fills the remaining part of the viewport below header */
  background: cyan;
  display: grid;
  grid-template: 1fr 1fr / 1fr 1fr;
  grid-gap: 2px;
}

.cell {
  padding: 10px;
  background: linear-gradient(to bottom right, orange, white);
}
<div class="container">
  <div class="header">Variable Height<br />Header</div>
  <div class="grid">
    <div class="cell a">Cell A</div>
    <div class="cell b">Cell B</div>
    <div class="cell c">Cell C</div>
    <div class="cell d">Cell D</div>
  </div>
</div>
Wednesday, November 3, 2021
 
Frank van Puffelen
answered 1 Month ago
73

With the restrictions you made: No, there isn't.

edit: Because you didn't mention this: You could use tables for this. (welcome back to the 90s)

Monday, November 8, 2021
 
CommonsWare
answered 1 Month ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :
 
Share