I am using CSS Grid in my layout...
How do I set different column size on first row and full width on second row?
Is this possible?
Thank you so much!!!
This is not possible, the idea of css grid is to have a "grid", that is all the rows follow the same column configuration.
What you can do however is have an element span several column, and here you have several options:
This one stretches from the first column to the first from the end
div:nth-child(3) {
grid-column: 1 / -1;
}
This one spans 2 columns from the first one:
div:nth-child(3) {
grid-column: 1 / span 2;
}
Related
This question already has answers here:
How to center elements on the last row in CSS Grid?
(8 answers)
Aligning grid items across the entire row/column (like flex items can)
(3 answers)
Closed 24 days ago.
I am working on a clients website and I have succesfully done this before but ONLY with one item. Now I am working on a grid with 43 elements in columns of 4. The last row has 3 items which I'd like centered. See here.
I have been stuck on this issue for a while and I belive I am just simply not understanding how the last child/n-th child relationship works
I have used THIS code before to center a singular item
selector .e-loop-item:last-child:nth-child(3n + 2) { grid-column-end: 3; }
Now I have tried code like
selector .e-loop-item:last-child:(41)(3n + 1) { grid-column-end: 11; }
selector .e-loop-item:last-child(42):nth-child(3n + 2) { grid-column-end: 11; }
selector .e-loop-item:last-child(43):nth-child(3n + 2) { grid-column-end: 11; }
See this fiddle.
Consider a grid of words in four columns, with each column being the same width. I can get this with grid-template-columns: repeat(auto-fill, minmax(25%, 1fr)); Most of the time, these will be short words. But there could be a long word which overflows its block:
If this were static data, I could have just changed the grid css to grid-template-columns: repeat(auto-fill, minmax(33%, 1fr)); to comfortably fill the space:
How can I make the CSS renderer adjust the number of columns for me, making each the same width, using the width of the widest word block in the set?
I'm surprised that CSS doesn't (as far as I can tell) support this since I think it should be a common use case for wrapping grids. I tinkered until I had a solution with a minimal amount of javascript:
example fiddle with IIFE
First, we need to find out how wide the widest string (or could be any block element) will be when rendered on the client. grid-template-columns: repeat(1, max-content); will do that by arranging the elements in a single column, and all elements get the width of the widest one. After the dimensions are established but before being rendered to the screen, grab any element, compute the number of columns that will fit, then change the CSS for the container to have that many columns + 1fr to make them fill the space:
(function(){
const ul = document.querySelectorAll('ul')[0];
const ee = [...document.querySelectorAll('li')];
const max = ee.map(e=>e.offsetWidth).reduce((a,b)=>Math.max(a, b));
const cols = Math.min(5,Math.floor(ul.offsetWidth/max));
ul.style.gridTemplateColumns = "repeat("+cols+", 1fr)";
})();
This seems to work with no flicker in all evergreen browsers. Angular version I used:
ngAfterViewInit(): void {
const ul: HTMLElement = this.ul.nativeElement;
let cols: number = 0;
const w: number = ul.children[0].clientWidth;
if (w > 0) {
cols = Math.min(5, Math.floor(ul.offsetWidth / w));
}
ul.style.gridTemplateColumns = "repeat(" + cols + ",1fr)";
}
As far as I know this is unsolvable with CSS-only, so, I will provide a Javascript-based solution.
First of all, let's find the longest text:
let longestText = '';
for (let text of texts) {
if (longestText.length < text.length) longestText = text;
}
Then, you can find that item:
let lng = document.querySelectorAll('ul li').filter((item) => item.innerText === longestText)[0].offsetWidth;
Then you can divide the whole width that you have at your disposal with lng, the value you have just calculated and see how many columns you can have.
This is a pretty specific issue and I wouldn't be surprised if it's impossible to solve. Let's say I have the following html, which I cannot change at all:
<box>
<tab class="A" id="A1">...</tab>
<tab class="A" id="A2">...</tab>
<tab class="A" id="A3">...</tab>
...
<tab class="B" id="B1">...</tab>
<tab class="B" id="B2">...</tab>
<tab class="B" id="B3">...</tab>
...
</box>
The amount of A and B tabs varies. Using only CSS, I would like to achieve the following behavior:
1: The tabs have to be arranged in rows, and wrap to the next row once they exceed the width of the box (which is only constrained by screen width). (This is the easy part. I already know how to do it with flex, but I'm not committed to one specific method.)
|A1 A2 A3 B1|
|B2 B3 |
2: But what I would like to see is for the B elements all wrap to the next line at once, as if they were one element. I also found a way to do this by inserting an invisible line break element with ::before and assigning it an "order" value that puts it between A3 and B1.
|A1 A2 A3 |
|B1 B2 B3 |
3: However, this means there are always a minimum of 2 lines, for each type of element. The behavior I'm trying to achieve is that if all elements do fit inside the width of the box, they should all remain on the same line, and only wrap in the manner described in 2 once the width is exceeded. Like this:
|A1 A2 B1 B2|
New tab is added:
|A1 A2 |
|B1 B2 B3 |
Without reformatting the HTML, implementing a few Javascript conditional if-statements might make it easier since you are still able to target the className directly.
If you are able to reformat the HTML in some way, it might be easier to control with creating 2 new HTML div elements in the box element (1 surrounding each different class type) in order to control easier with flex-box.
I found a solution. Assuming that the width of the box fits exactly 4 tabs (This should be mathematically generalizable to other cases of course. My actual case is much more complicated, with the box width fitting roughly 10 B-tabs, and B-tabs being 4 times as wide as A-tabs. I'm simplifying here for comprehensibility.):
The box is styled as a flex-box:
box {
display: flex;
flex-wrap: wrap;
overflow-y: auto;
}
This inserts the line break with order value 1 (by default this is sorted after elements without the order property):
box::after {
display: flow-root list-item;
order: 1;
content: "";
flex-basis: -moz-available;
height: 0px;
}
This tells every B element immediately after an A element to be sorted after the line break iff A and B elements together exceed the line (this will require at minimum as many comma-separated selectors as the number of B-tabs that can be fit into the width of the box):
.A:nth-child(n+4) + .B:nth-last-of-type(n+1),
.A:nth-child(n+3) + .B:nth-last-of-type(n+2),
.A:nth-child(n+2) + .B:nth-last-of-type(n+3),
.A:nth-child(n+1) + .B:nth-last-of-type(n+4)
{ order: 2; }
This tells the subsequent elements to do the same:
.A:nth-child(n+4) + .B:nth-last-of-type(n+1) ~ *,
.A:nth-child(n+3) + .B:nth-last-of-type(n+2) ~ *,
.A:nth-child(n+2) + .B:nth-last-of-type(n+3) ~ *,
.A:nth-child(n+1) + .B:nth-last-of-type(n+4) ~ *
{ order: 2; }
Theoretically it should be possible to combine these two blocks via nesting:
.A:nth-child(n+4) + .B:nth-last-of-type(n+1),
.A:nth-child(n+3) + .B:nth-last-of-type(n+2),
.A:nth-child(n+2) + .B:nth-last-of-type(n+3),
.A:nth-child(n+1) + .B:nth-last-of-type(n+4)
{ order: 2;
~ * { order: 2; }
}
However, in my case that didn't work. I'm trying to style my browser UI, and apparently it doesn't accept nesting.
If anyone has a suggestion for how to further streamline this, it is more than welcome.
I am trying to make a simple sidebar layout with a flat HTML structure, in which the first <aside> element fills in the 1st column completely.
My problem is, that the negative row end value seems to not work for the implicitly created rows for all my elements in the 2nd column.
Expected:
Actual:
Below is a runnable code snippet that illustrates the problem.
article {
display: grid;
grid-template-columns: 200px 1fr;
background: gray;
}
aside {
grid-row: 1/-1;
grid-column: 1/2;
background: pink;
}
section {
grid-column: 2/3;
background: yellow;
}
<article>
<aside>In the left column (top to bottom)</aside>
<section>In the right column</section>
<section>In the right column</section>
<section>In the right column</section>
</article>
You can only use negative integers in an explicit grid.
See the Grid spec here:
7.1. The Explicit
Grid
Numeric indexes in the grid-placement properties count from the edges
of the explicit grid. Positive indexes count from the start side
(starting from 1 for the start-most explicit line), while negative
indexes count from the end side (starting from -1 for the end-most
explicit line).
and here...
8.3. Line-based Placement: the grid-row-start, grid-column-start, grid-row-end, and grid-column-end properties
If a negative integer is given, it instead counts in reverse, starting
from the end edge of the explicit grid.
Making a grid area span an entire column / row, including implicit tracks, when the number of tracks in the grid is unknown, is not possible in CSS Grid Level 1, unless you want to try to hack it.
What does the CSS3 style grid-column-start: 2; do?
Please try to answer from the following:
• Generates grid with 2 columns
• Creates a grid item on second column
• Starts grid item from second column
• Creates a 2×2 grid
It determines a grid item's location within the grid by referring to specific grid lines.
grid-column-start/grid-row-start is the line where the item begins, and grid-column-end/grid-row-end is the line where the item ends.
For Your case suppose following css property
.item-a{
grid-column-start: 2;
grid-column-end: five;
grid-row-start: row1-start
grid-row-end: 3
}
The output will be See this image
Starts grid item from second column