Show a list of items as grid or column depending on the device - angular-flex-layout

I have a basic need.
I have n images I want to display. I want to display them in rows, each row containing 3 images, if I am on a browser on a computer. I want to show them in a column, i.e. one below the other, if I am on a mobile device.
I do not want to use bootstrap.
Currently I am thinking to manage this with angular/flex-layout, but is seems that there is a bit of work (e.g. find the number of rows dividing by 3 and so on).
I am wondering if there is a simpler way.

This is very easily done with css media queries and flexbox.
.item {
width: 50px;
height: 50px;
background-color: tomato;
width: 100%;
margin: 8px;
}
#media (min-width: 720px) {
.list {
display: flex;
flex-wrap: wrap;
}
.item {
width: calc(33% - 16px);
}
}
<div class="list">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<!-- however many items you want -->
</div>
Live demo

Related

Float layout with reordering in modern CSS

I used to be good at CSS at one point, but the ship has sailed on while I was away.
I need some guidance on how to solve the following case:
I have X number of divs where X may change over time
The divs "float left" inside a container div, wrapping into rows
All the divs have their own rigid width and height
The divs have an order that can change and that is important
It is desirable that the rows formed by the float layout dont create uneccessary "gaps" (see the marked areas in example below)
So my html is like this:
<div class="container">
<div id="item-1" class="item"></div>
<div id="item-2" class="item"></div>
<div id="item-3" class="item big"></div>
<div id="item-4" class="item"></div>
<div id="item-5" class="item"></div>
<div id="item-6" class="item"></div>
<div id="item-7" class="item big"></div>
<div id="item-8" class="item"></div>
<div id="item-9" class="item"></div>
<div id="item-a" class="item"></div>
<div id="item-b" class="item"></div>
<div id="item-c" class="item"></div>
<div id="item-d" class="item"></div>
</div>
And my naïve pseudo-CSS is like this:
.container {
display: block;
width: 600px;
height: 600px;
border: 2px solid black;
}
.item {
width: 100px;
height: 100px;
border: 2px solid blue;
float: left;
}
.item.big {
width: 150px;
height: 110px;
border: 2px solid red;
}
So my question is, are there any modern CSS primitives that would solve this case? Especially, can the order be handeled in pure CSS?
You can achieve this with Flexbox . Flexbox is a pure CSS solution for that kind of problems.
All modern browsers supports it, so you can get rid of the float in your CSS files.
For better understanding, you can read about it here.
Hope it helps.

Flex: How do I set items to take up MAX 1/3 of their container, unless their content overflows

Let's say I have a flex container, I want to be display between 1 and 3 items within.
I would like the items to be:
By default 1/3 of the width of the container (even if there's only 1 item)
If one of the item's text content expands slightly beyond the 33% width, I'd like that item to try and expand if it can (i.e. by one of the other items shrinking).
Justified using flex-start
.container {
display: 'flex';
flex-direction: 'row';
background-color: 'red';
width: '100%';
justify-content: 'flex-start';
}
.item {
flex: 1;
border: 1px solid red;
flex-wrap: 'no-wrap';
}
<div class="container">
<div class="item">blahhhhhhhhhhhhhhhhhhhhhhhhhh</div>
<div class="item">blahhh</div>
<div class="item">blahhh</div>
</div>
<div class="container">
<div class="item">I should take up 1/3</div>
</div>
The above works for the 3 item case, however when a singular item is placed there it expands to fill the space.
Is this possible?
You can use CSS-Grid for that.
.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
margin-bottom: 1em;
}
.item {
height: 50px;
border: 1px solid red;
}
.item.wide {
width: 250px;
}
<div class="container">
<div class="item wide"> I'm wider than 1/3</div>
<div class="item"></div>
<div class="item"></div>
</div>
<div class="container">
<div class="item"></div>
</div>
<div class="container">
<div class="item"></div>
<div class="item"></div>
</div>

How can I make a responsive css grid (display: grid) that has three equal boxes on the top row and then the rest of the rows are two equal size boxes?

I want to make a responsive css grid that looks like this:
box | box | box
b o x | b o x
b o x | b o x
and then when the screen size gets small enough, all of the columns collapse into a single column with each box on its own row.
Is this possible?
Edit:
For anyone who has this issue, changing the number of columns via media query from 6 to 1 was not working. However, I had the idea to make all of the items span 6 columns at the break point and that worked perfectly. This is an addition to the answer given by lawrence-witt and paulie-d.
By using a SIX column grid
.item {
height: 3em;
border: 2px solid green;
}
.container {
display: grid;
grid-template-columns: repeat(6, 1fr);
grid-gap: 4px;
width: 90%;
margin: 1em auto;
}
.item:nth-child(1),
.item:nth-child(2),
.item:nth-child(3) {
grid-column: span 2
}
.item:nth-child(4),
.item:nth-child(5) {
grid-column: span 3;
}
<div class="container">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
After that it's just a matter of adding a media query to collapse the grid to a single column at the appropriate breakpoint,
Here is one way to do it using the nth-child feature. To make the grid responsive you simply add a media query that changes the styling of cells and grid at a certain screen width.
.grid {
display: grid;
grid-template-columns: repeat(6, 1fr);
}
.cell {
border: 1px solid black;
grid-column: span 3;
}
.cell:nth-child(1),
.cell:nth-child(2),
.cell:nth-child(3){
grid-column: span 2;
}
<div class="grid">
<div class="cell">One</div>
<div class="cell">Two</div>
<div class="cell">Three</div>
<div class="cell">Four</div>
<div class="cell">Five</div>
<div class="cell">Six</div>
<div class="cell">Seven</div>
</div>
By using CSS flex layout and CSS media query you can achieve your need. See the below code, if the screen size reduced to 600px then it will change to single column layout.
.container {
display: flex;
width: 100%;
justify-content: space-between;
flex-direction: row;
margin-bottom: 10px;
}
.three-box-row > div {
width: 33%;
height: 50px;
}
.two-box-row > div {
width: 49%;
height: 50px;
}
#media only screen and (max-width: 600px) {
.container {
flex-direction: column;
}
.container > div {
width: 100%;
}
}
<body>
<div class="three-box-row container">
<div style="background-color: red;">Box 1</div>
<div style="background-color: green;">Box 2</div>
<div style="background-color: blue;">Box 3</div>
</div>
<div class="two-box-row container">
<div style="background-color: red;">Box 1</div>
<div style="background-color: green;">Box 2</div>
</div>
<div class="two-box-row container">
<div style="background-color: green;">Box 1</div>
<div style="background-color: blue;">Box 2</div>
</div>
</body>

Adding a class "collapse" to flex grid creates uneven spacing

So, I am creating a grid system based on flexbox and everything is going quite swimmingly. The basics of my grid are:
<div class="row">
<div class="column"><p>Column</p></div>
<div class="column"><p>Column</p></div>
<div class="column"><p>Column</p></div>
</div>
And in my css:
.row {
margin: 10px 0;
display: flex;
flex-wrap: wrap;
}
.column {
padding: 10px;
flex: 1 1 0%;
}
Essentially, this makes the columns quite fluid, and they shrink/grow to fill all available space. This is great for me as I need to use this throughout various projects where I can't quite customize the grid for every single one. However, I have run into a small "issue". I was going to create a class called ".collapse" so I could collapse the left/right padding to have some columns fit right next together (for example: If I wanted a div with a background color (by adding a color class to the column=> .column .green) flush to an image in the next column). However, the spacing is all out of wack compared to row/columns above it.
<div class="row">
<div class="column purple collapse"><p>Column</p></div>
<div class="column red collapse"><p>Column</p></div>
<div class="column purple collapse"><p>Column</p></div>
<div class="column red collapse"><p>Column</p></div>
</div>
example screenshot here
As you can see in my little example mockup, they do kinda line up, but the right and left margins have "decreased". Is there any smart way around this? I tried adding "left/right margins" to the first-of-type and last-of-type, but this just gets a bit hacky as then anything added in between start having odd alignment issues.
For this kind of grid system, you usually would discourage using structural styling on the grid cells directly, and it lets you do something like this:
.row {
display: flex;
flex-flow: row wrap;
list-style: none;
padding: 0;
margin-left: -10px;
}
.column {
flex: 1 0 0;
padding-left: 10px;
}
.collapse { margin-left: 0; }
.collapse > .column { padding-left: 0; }
.red,
.purple {
padding: 10px;
margin-bottom: 10px;
}
.red { background-color: red; }
.purple { background-color: purple; }
<div class="row">
<div class="column">
<div class="purple">
<p>Column</p>
</div>
</div>
<div class="column">
<div class="red">
<p>Column</p>
</div>
</div>
<div class="column">
<div class="purple">
<p>Column</p>
</div>
</div>
<div class="column">
<div class="red">
<p>Column</p>
</div>
</div>
</div>
<div class="row collapse">
<div class="column">
<div class="purple">
<p>Column</p>
</div>
</div>
<div class="column">
<div class="red">
<p>Column</p>
</div>
</div>
<div class="column">
<div class="purple">
<p>Column</p>
</div>
</div>
<div class="column">
<div class="red">
<p>Column</p>
</div>
</div>
</div>
This approach uses no margins on the outer ends, which I find way more convenient.
It's worth noting that this kind os system is not all that useful anymore, with the advent of CSS Grid Layout, but there you have it.
On a side note, 0 is always 0, and it never needs a unit.

Incorrect transform position in Chrome when using percent

When using transform with percent value on container that has float width (for ex. 800.63px) Chrome always rounds position in pixels incorrectly.
This typically happens with em/rem width in combination with percent (see example below):
HTML:
<div class="container">
<div class="wrap">
<div class="slider">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
</div>
</div>
SCSS:
.container {
width: 38rem;
}
.wrap {
width: 33%;
overflow: hidden;
}
.slider {
white-space: nowrap;
font-size: 0;
transform: translate3d(-1000%,0,0);
}
.item {
display: inline-block;
height: 8rem;
width: 100%;
background: limegreen;
&:nth-child(even) {
background: orangered;
}
}
The result is some part of next slide is always visible. Looks like Chrome first rounds the item width and then multiplies it by percent value.
Is there any known workaround for this? If I do the math in JS and use px values in transform then everything is (almost) perfect, but shouldn't it "just work" with percents too?
Please, see fiddle for working example: https://jsfiddle.net/Lumh07te/37/
Change the way you set your sizes.
Instead of the slider having width 100%, and needing a maximum transform of 1000%, set it to a width of 1000%, and a needed maximum transform of 100%.
And the items width is now 10% instead of 100%
in your original code, wrap has a width that is not integer, but than in the layout is forced to render as an integer value in pixels. Then , translating a 1000% of this value multiplies the rounding errors by 10. If you instead set the width to 1000%, the rounding to pixels is done on this size, and then it is mutiplyed by values less than 1 (0.5, 0.6, 0.7.. ) or 1 at a maximum
.container {
width: 38rem;
}
.wrap {
width: 33%;
overflow: hidden;
}
.slider {
width: 1300%;
white-space: nowrap;
font-size: 0;
transform: translateX(calc(-500% / 13));
transition: transform 0.4s;
}
.test {
height: 50px;
}
.test:hover ~ .wrap .slider {
transform: translateX(calc(-800% / 13));
}
.item {
display: inline-block;
height: 8rem;
width: calc(100% / 13);
background: limegreen;
font-size: 30px;
}
.item:nth-child(even) {
background: orangered;
}
<div class="container">
<div class="test">test</div>
<div class="wrap">
<div class="slider">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</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 class="item">11</div>
<div class="item">12</div>
<div class="item">13</div>
</div>
</div>
</div>

Resources