Css-grid: how to add block and make other elements move up? - css

I have 2 condition of layout.
(1)
(2)
I want to dynamically delete or add block 2 and switch between layouts. Is it possible to deal with it via css-grid?

CSS grid is intended to define grid layouts.
Il will allow you to define as many layouts as you want, but you will not be able to switch between layouts by relying only on CSS grids.
You will have to handle it programmatically, or by using other CSS features (like media queries, if you want layout selection based on screen width).
A way to do this is to define the grid layouts with specific CSS classes on the grid container, and switch between the classes using an external script (or a condition based on the state of your app if it's already there).
Demo :
https://jsbin.com/jolupoguni/1/edit?html,css,js,output
JS :
const toggleLayout = () => {
var element = document.getElementById("grid");
if (element.classList.contains('grid--layout-1')) {
element.classList.remove('grid--layout-1');
element.classList.add('grid--layout-2');
} else {
element.classList.remove('grid--layout-2');
element.classList.add('grid--layout-1');
}
}
CSS :
.grid {
display: grid;
grid-template-columns: repeat(12, 1fr);
}
.grid--layout-1 .block-a {
grid-column: span 12;
}
.grid--layout-1 .block-b {
grid-column: span 6;
}
.grid--layout-1 .block-c {
grid-column: span 6;
}
.grid--layout-2 .block-a {
grid-column: span 12;
}
.grid--layout-2 .block-b {
display: none
}
.grid--layout-2 .block-c {
grid-column: span 12;
}
HTML
<button onclick="toggleLayout()">Switch layout</button>
<div id="grid" class="grid grid--layout-1">
<div class="block block-a">Block A</div>
<div class="block block-b">Block B</div>
<div class="block block-c">Block C</div>
</div>
For more information about CSS grids, Wes Bos' CSS Grid course is a fantastic resource https://cssgrid.io/

Related

How to prevent CSS Flex / Grid layouts to be reversed on dir="RTL"

I have a simple div (grid layout but the same would apply to flexbox), which shows three different text elements.
The problem is that whenever my html's dir attribute is set to rtl, the order of the grid items changes, and I'd like to be able to prevent that change in that specific case (where content order is not based on writing direction, but for semantic value.
For example's sake, here's what my items look like:
let direction = 'rtl';
document.getElementById('toggle-direction').addEventListener('click', () => {
direction = direction === 'rtl' ? 'ltr' : 'rtl';
document.documentElement.setAttribute('dir', direction);
});
.container {
display: grid;
grid-template-rows: 1fr;
grid-template-columns: repeat(3, 1fr);
border-radius: 5px;
padding: 10px;
}
.left-arrow {
grid-column: 1 / 2;
grid-row: 1 / 2;
margin-inline-end: auto;
}
.center-content {
grid-row: 1 / 2;
grid-column: 2 / 3;
text-align: center;
}
.right-arrow {
grid-row: 1 / 2;
grid-column: 3 / 4;
margin-inline-start: auto;
}
#toggle-direction {
grid-row: 2 / 3;
grid-column: 1 / 4;
}
<div class="container">
<span class="left-arrow"><</span>
<span class="center-content">Center content</span>
<span class="right-arrow">></span>
<button id="toggle-direction">Toggle direction</button>
</div>
PS: I could add direction: ltr; to the .container selector, but that would create an undesired styling, since I want to use inline-oriented styling.
Is my understanding you only want the arrows to keep their orientation.
So you should isolate and wrap the elements you want the direction to consistent in containers with consistent flow direction (and put everything you want to update outside that containers) like so :
let direction = 'rtl';
document.getElementById('toggle-direction').addEventListener('click', () => {
direction = direction === 'rtl' ? 'ltr' : 'rtl';
document.documentElement.setAttribute('dir', direction);
});
.container {
display: grid;
grid-template-rows: 1fr;
grid-template-columns: repeat(3, 1fr);
}
.ltr {
direction: ltr;
}
.left-arrow {
grid-column: 1 / 2;
text-align: center;
}
.center-content {
grid-column: 2 / 3;
text-align: center;
}
.right-arrow {
grid-column: 3 / 4;
text-align: center;
}
#toggle-direction {
grid-column: 1 / 4;
}
<!-- inherit direction flow -->
<div class="container ltr">
<!-- ltr direction flow -->
<p class="left-arrow">Left arrow</p>
<p class="center-content">Center content</p>
<p class="right-arrow">Right arrow</p>
</div>
<div class="container">
<!-- inherit direction flow -->
<button id="toggle-direction">Toggle direction</button>
</div>
Edits done: wrapped the first row you had inside the container in another container (class="colum rtl") that always has a direction.
Small css updates to keep the view.
Alternatively you could place the arrows container inside the container with the button. But in order to have consistent rtl direction you will need to wrap them there in a container (with class="rtl") and you need a little more updates to your css to keep the aspect.
Alternatively if there is really no way for you to isolate all the components you need to have consistent direction you could for some specific cases create a class class="inherit-direction" (for example) and in your js, when you change the direction of the document, change the containers direction with that class as well (document.getElementsByClassName("inherit-direction") and iterate trough them).
But I would strongly advise to use this last option only as last measure and limit the number of uses as much as possible.

Css Sorting and categorization of random data

Say you have the following data points that are dumped from a database or file, the size of each item representing the amount of something (hours, pizzas, whatever)...
And you would like to organize the data into categories like so using CSS...
How would you do it?
I tried the grid shepard method using grid-rowbut get this instead...
Thanks!
P.S. follow up question... My intuition is that CSS would be faster than JS (...moving each item into the appropriate flexbox that could then sort row members horizontally). Is that true? Is CSS likely to be faster in a dataset of possibly thousands of elements?
You can use the flexbox with order.
This is a guide for the flexbox: https://css-tricks.com/snippets/css/a-guide-to-flexbox/
check this sample:
.wrapper {
display: flex;
flex-flow: row wrap;
}
.wrapper > * {
padding: 10px;
flex: 1 0 100%;
}
.o-1,.o-3,.o-5,.o-7 {
background: gold;
}
.o-2,.o-4,.o-6 {
background: hotpink;
}
.o-1 { order: 1; }
.o-2 { order: 2; }
.o-3 { order: 3; }
.o-4 { order: 4; }
.o-5 { order: 5; }
.o-6 { order: 6; }
.o-7 { order: 7; }
<div class="wrapper">
<aside class="o-1">1</aside>
<aside class="o-3">3</aside>
<aside class="o-2">2</aside>
<aside class="o-4">4</aside>
<aside class="o-7">7</aside>
<aside class="o-5">5</aside>
<aside class="o-6">6</aside>
</div>

How to specify the maximum number of columns repeat() will create using auto-fit/fill? [duplicate]

This question already has answers here:
CSS grid - maximum number of columns without media queries
(3 answers)
Closed 2 years ago.
Is there a way, without using media query, to limit the number of columns that get created, and combine that with auto-fit/fill?
For example:
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
Creates a wonderful, responsive layout. But imagine the design calls for a maximum of 3 columns -- even in wide browsers. It would happily show, 4, 5, more columns as space allows.
Using media query, I could specify big screens get:
grid-template-columns: 1fr 1fr 1fr;
And smaller screens get:
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
But that feels wrong. Is there a way to tell my grid layout to repeat up to a maximum of n times, and still use repeat and auto-fit/fill?
Update: This is not a duplicate of: How to limit the amount of columns in larger viewports with CSS Grid and auto-fill/fit?
I am asking how to make the code for the viewport also work for smaller viewports. The question (and answer) referenced above only gets to the starting point of my question.
CSS custom properties ('CSS variables') to the rescue
.grid {
--repeat: auto-fit;
}
#media (min-width: calc(250px * 3)) {
.grid {
--repeat: 3;
}
}
.grid {
display: grid;
grid-template-columns: repeat(var(--repeat, auto-fit), minmax(calc(250px * 1) , 1fr));
grid-gap: 8px;
}
.grid .item {
background-color: silver;
padding: 8px;
}
<div class="grid">
<div class="item">Lorem, ipsum.</div>
<div class="item">Soluta, voluptatibus!</div>
<div class="item">Reprehenderit, consequuntur.</div>
<div class="item">Temporibus, veritatis!</div>
<div class="item">Consequatur, voluptates.</div>
<div class="item">Distinctio, adipisci.</div>
<div class="item">Repellat, corrupti.</div>
<div class="item">Quia, corporis.</div>
<div class="item">Nobis, aut.</div>
<div class="item">Dicta, officiis?</div>
<div class="item">Voluptate, tempora?</div>
<div class="item">Nihil, earum?</div>
<div class="item">Placeat, aspernatur!</div>
<div class="item">Officia, sunt?</div>
<div class="item">Atque, temporibus!</div>
<div class="item">Rerum, unde!</div>
<div class="item">Hic, molestias!</div>
<div class="item">Et, repellat!</div>
<div class="item">Earum, itaque.</div>
<div class="item">Doloribus, facilis.</div>
<div class="item">Eius, alias!</div>
<div class="item">Est, officia.</div>
<div class="item">Ad, porro!</div>
<div class="item">Ipsum, voluptates.</div>
<div class="item">Animi, eligendi.</div>
<div class="item">Tempore, hic!</div>
<div class="item">Voluptatibus, illum.</div>
<div class="item">Autem, cumque!</div>
<div class="item">Cupiditate, minus!</div>
<div class="item">Tenetur, aliquam.</div>
</div>
Remark
The asker wanted in his OP a solution which would not use media queries. This solution does use media queries, but in a somewhat different way. The only thing that is changed in the media query is the value of a CSS custom property.
We could even use a 'global' custom property with the :root selector
:root {
--repeat: auto-fit;
}
#media (min-width: calc(250 * 3px)) {
:root {
--repeat: 3;
}
}
One could think further and use custom properties in the media query condition itself, but unfortunately this does not work. (See this answer)
/* not working */
:root {
--repeat: auto-fit;
--max-columns: 3;
--max-column-width: 250px;
}
/* Will not work: var is not allowed in media query condition */
#media (min-width: calc(var(--max-column-width) * var(--max-columns))) {
:root {
--repeat: var(--max-columns);
}
}
Just add a max-width to the container.
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
max-width: 1280px;

CSS Grid auto placement in IE/EDGE

While using the old CSS grid spec that is supported by IE 11 and EDGE. Is it possible for the grid items to be auto placed like the current spec?
i.e. to not have to define the column on a grid item:
.item:nth-child(1) {
-ms-grid-column: 1;
}
.item:nth-child(2) {
-ms-grid-column: 2;
}
.item:nth-child(n) {
-ms-grid-column: n;
}
https://codepen.io/JoeHastings/pen/mMPoqB
The answer is NO (unfortunately).
Old specs section about auto-placement has such preamble
This section describes early thinking around automatic placement of Grid Items. Multiple algorithms are possible for such a feature. One is proposed here.
Run this code in IE/Edge and you'll see a lot of rows with 1 in console because IE/Edge stacks all grid items in first cell and you can't force IE/Edge to place grid items automatically. Setting -ms-grid-column and -ms-grid-row to auto won't change anything, because this value is not supported (as you can see in MSDN links). Demo:
var gridItems = document.querySelectorAll(".grid__item");
for (var i = 0; i < gridItems.length; i++) {
var gridItem = gridItems[i];
console.log(window.getComputedStyle(gridItem)["-ms-grid-row"]);
console.log(window.getComputedStyle(gridItem)["-ms-grid-column"]);
}
.grid {
display: -ms-grid;
-ms-grid-columns: 100px 100px 100px;
-ms-grid-rows: 100px 100px 100px;
}
.grid__item {
-ms-grid-row: auto;
-ms-grid-column: auto;
background-color: tomato;
color: white;
font-size: 20px;
display: flex;
align-items: center;
justify-content: center;
}
<div class="grid">
<div class="grid__item">One</div>
<div class="grid__item">Two</div>
<div class="grid__item">Three</div>
<div class="grid__item">Four</div>
<div class="grid__item">Five</div>
<div class="grid__item">Six</div>
<div class="grid__item">Seven</div>
<div class="grid__item">Eight</div>
<div class="grid__item">Nine</div>
</div>

How to exclude a div (flex item) from flex calculations?

I have an html layout that I cannot modify since it's a 3d party library.
I have four divs and I want them to be inside a flexbox like that:
1------2------3
(where 2 is in the center and 4 is not visible at all).
I have created an example here.
It seems that it mostly work as I want to apart from the fact that the last div messes up the center position of the item number 2. Is there a way I can completely "exclude" it from the flex array by css even though it is in HTML?
.fc-toolbar {
background-color: #7CC2DD;
color: white;
height: 5rem;
display: flex;
align-items: center;
justify-content: space-between;
}
.fc-left {
order: 0;
}
.fc-right {
order: 2;
}
.fc-center {
order: 1;
}
.fc-clear {
}
<div class="fc-toolbar">
<div class="fc-left">
<button type="button">
<
</button></div>
<div class="fc-right">
<button type="button">
>
</button>
</div>
<div class="fc-center">
<h2>May 2016</h2>
</div>
<div class="fc-clear"></div>
</div>
You can give the element position: absolute and it will be taken out of the flow, just as you would do with a non-flex element.
You can find the updated jsfiddle here.

Resources