6

EDIT Keep in mind that each cell can have a different width and height. This is not the same thing as this post: CSS-only masonry layout, see guide lines of the reference picture:

enter image description here

there are about 19 columns and 17 rows made by guide lines and tiles placed in virtual 5×5 base grid overlap it in both axis.

I want something between a grid and a flex layout. Grids are limited by cell size and flex is more powerful, but (what I know of it) is limited to direction. I want to have different cell sizes, each 5 of them summing to the same width, and 5 columns summing to the same height. Like the image below.

Is there any way of achieving a similar layout using CSS?

enter image description here

This is all I got until now:

HTML:

<div class="calendar">
  <div class="day day1">1</div>
  <div class="day day2">2</div>
  <div class="day day3">3</div>
  <div class="day day4">4</div>
  <div class="day day5">5</div>
  <div class="day day6">6</div>
  <div class="day day7">7</div>
  <div class="day day8">8</div>
  <div class="day day9">9</div>
  <div class="day day10">10</div>
  <div class="day day11">11</div>
  <div class="day day12">12</div>
  <div class="day day13">13</div>
  <div class="day day14">14</div>
  <div class="day day15">15</div>
  <div class="day day16">16</div>
  <div class="day day17">17</div>
  <div class="day day18">18</div>
  <div class="day day19">19</div>
  <div class="day day20">20</div>
  <div class="day day21">21</div>
  <div class="day day22">22</div>
  <div class="day day23">23</div>
  <div class="day day24">24</div>
  <div class="day day25">25</div>
</div>

CSS:

.day {
  margin: 10px;
  color: white;
}

.calendar {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-content: flex-start;
  width: 586px;
  height: 586px;
  border: solid 1px;
}

.day1 {
  width: 87px;
  height: 97px;
  background: lightblue;
}

.day2 {
  width: 151px;
  height: 86px;
  background: orange;
}

.day3 {
  width: 86px;
  height: 86px;
  background: lightcoral;
}

.day4 {
  width: 76px;
  height: 118px;
  background: lightgray;
}

.day5 {
  width: 86px;
  height: 86px;
  background: lightblue;
}

.day6 {
  width: 87px;
  height: 86px;
  background: lightsteelblue;
}

.day7 {
  width: 108px;
  height: 97px;
  background: lightblue;
}

.day8 {
  width: 129px;
  height: 97px;
  background: lightsteelblue;
}

.day9 {
  width: 76px;
  height: 65px;
  background: orange;
}

.day10 {
  width: 86px;
  height: 128px;
  background: cyan;
}

.day11 {
  width: 75px;
  height: 75px;
  background: lightcoral;
}

.day12 {
  width: 99px;
  height: 96px;
  background: lightgray;
}

.day13 {
  width: 87px;
  height: 96px;
  background: lightcyan;
}

.day14 {
  width: 139px;
  height: 96px;
  background: orange;
}

.day15 {
  width: 86px;
  height: 65px;
  background: lightcoral;
}

.day16 {
  width: 75px;
  height: 118px;
  background: orange;
}

.day17 {
  width: 88px;
  height: 97px;
  background: lightcoral;
}

.day18 {
  width: 161px;
  height: 97px;
  background: cyan;
}

.day19 {
  width: 98px;
  height: 118px;
  background: lightgreen;
}

.day20 {
  width: 64px;
  height: 97px;
  background: lightgray;
}

.day21 {
  width: 108px;
  height: 97px;
  background: lightsteelblue;
}

.day22 {
  width: 150px;
  height: 97px;
  background: lightblue;
}

.day23 {
  width: 65px;
  height: 97px;
  background: lightgray;
}

.day24 {
  width: 98px;
  height: 75px;
  background: orange;
}

.day25 {
  width: 65px;
  height: 97px;
  background: lightblue;
}

https://codepen.io/jonathascosta/pen/yLzPPxz

myf
  • 9,874
  • 2
  • 37
  • 49
Jonathas Costa
  • 1,006
  • 9
  • 27
  • 1
    Anything you can do with flex you can do with grid. In fact they work best together. Use grid for your overall layout and flex inside the grid cells or areas. – morganney Dec 25 '21 at 15:07
  • You could manually set absolute positionning to each images, while keeping % values to be responsive. Does that sounds like a solution? – Jean Will Dec 25 '21 at 15:10
  • use grid the best solution for this – Laaouatni Anas Dec 25 '21 at 15:24
  • @LaaouatniAnas the columns and the rows have different sizes, is it possible to achieve this layout with grid? How? – Jonathas Costa Dec 25 '21 at 15:58
  • @JonathasCosta I answered you late because while I was writing the code for you. sorry for late answer – Laaouatni Anas Dec 25 '21 at 16:15
  • 1
    I don't think it's possible to be done by CSS. If you try to see for example Google's results with images, you'll see that they have the same height. It's easy to draw the image you showed by Photoshop, but HTML and CSS use box model. The only solution I see is to align each of your boxes manually to get this result. However I doubt this is what you ask. – Azu Dec 25 '21 at 16:20
  • @Azu i answered now with possible solution – Laaouatni Anas Dec 25 '21 at 16:27

3 Answers3

2

basically use CSS GRID

new answer

complete explanation in the previous answer below...

use also negative margin for the top ones like (1,4,19) and positive margin for the bottom ones

... 

/* here one example */
.day1 {
  grid-column: 1/2;
  margin: 0 0 -2vh 0;
}

.day6 {
  grid-column: 1/2;
  margin: 2vh 0 0 0;
}

...

enter image description here

the new code

body {
  display: grid;
  align-content: center;
  justify-content: center;
  height: 100vh;
}

.calendar {
  display: grid;
  gap: 0.5em;
  height: 80vh;
  width: 80vh;
}

.day {
  border: 3px solid goldenrod;
  padding: 0.1em;
  display: grid;
  place-items: center;
  border-radius: 0.3em;
  transition-duration: 1s;
}

.day:hover {
  background-color: goldenrod;
  color: white;
  transition-duration: 0.5s;
}

.day1 {
  grid-column: 1/2;
  margin: 0 0 -2vh 0;
}

.day2 {
  grid-column: 2/4;
}

.day3 {
  grid-column: 4/5;
}

.day4 {
  grid-column: 5/6;
  margin: 0 0 -6vh 0;
}

.day5 {
  grid-column: 6/7;
}

.day6 {
  grid-column: 1/2;
  margin: 2vh 0 0 0;
}

.day7 {
  grid-column: 2/3;
}

.day8 {
  grid-column: 3/5;
}

.day9 {
  grid-column: 5/6;
  margin: 6vh 0 0 0;
}

.day10 {
  grid-column: 6/7;
}

.day11 {
  grid-column: 1/2;
}

.day12 {
  grid-column: 2/3;
}

.day13 {
  grid-column: 3/4;
}

.day14 {
  grid-column: 4/6;
}

.day15 {
  grid-column: 6/7;
}

.day16 {
  grid-column: 1/2;
}

.day17 {
  grid-column: 2/3;
}

.day18 {
  grid-column: 3/5;
}

.day19 {
  grid-column: 5/6;
  margin: 0 0 -3vh 0;
}

.day20 {
  grid-column: 6/7;
}

.day21 {
  grid-column: 1/2;
}

.day22 {
  grid-column: 2/4;
}

.day23 {
  grid-column: 4/5;
}

.day24 {
  grid-column: 5/6;
  margin: 3vh 0 0 0;
}

.day25 {
  grid-column: 6/7;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" href="style.css">
</head>

<body>
  <div class="calendar">
    <div class="day day1">1</div>
    <div class="day day2">2</div>
    <div class="day day3">3</div>
    <div class="day day4">4</div>
    <div class="day day5">5</div>
    <div class="day day6">6</div>
    <div class="day day7">7</div>
    <div class="day day8">8</div>
    <div class="day day9">9</div>
    <div class="day day10">10</div>
    <div class="day day11">11</div>
    <div class="day day12">12</div>
    <div class="day day13">13</div>
    <div class="day day14">14</div>
    <div class="day day15">15</div>
    <div class="day day16">16</div>
    <div class="day day17">17</div>
    <div class="day day18">18</div>
    <div class="day day19">19</div>
    <div class="day day20">20</div>
    <div class="day day21">21</div>
    <div class="day day22">22</div>
    <div class="day day23">23</div>
    <div class="day day24">24</div>
    <div class="day day25">25</div>
  </div>
</body>

</html>

previous answer

one time I put the display: grid to the parent element (in this case .calendar)

now i can use in all day1, day2, day3...

...this css property grid-column

that is a shorthand for grid-column-start and grid-column-end

using the FireFox DevTools i click the grid button in the code, that make appear me a grid visualizer...

in the paper, I start thinking about... and I find that the best way is creating a 7 columns grid-based.

enter image description here

here the previus code

body {
  display: grid;
  align-content: center;
  justify-content: center;
  height: 100vh;
}

.calendar {
  display: grid;
  gap: 0.5em;
  height: 80vh;
  width: 80vh;
}

.day {
  border: 2px solid goldenrod;
  padding: 0.1em;
  display: grid;
  place-items: center;
  border-radius: 0.3em;
  transition-duration: 1s;
}

.day:hover {
  background-color: goldenrod;
  color: white;
  transition-duration: 0.5s;
}

.day1 {
  grid-column: 1/2;
}

.day2 {
  grid-column: 2/4;
}

.day3 {
  grid-column: 4/5;
}

.day4 {
  grid-column: 5/6;
}

.day5 {
  grid-column: 6/7;
}

.day6 {
  grid-column: 1/2;
}

.day7 {
  grid-column: 2/3;
}

.day8 {
  grid-column: 3/5;
}

.day9 {
  grid-column: 5/6;
}

.day10 {
  grid-column: 6/7;
}

.day11 {
  grid-column: 1/2;
}

.day12 {
  grid-column: 2/3;
}

.day13 {
  grid-column: 3/4;
}

.day14 {
  grid-column: 4/6;
}

.day15 {
  grid-column: 6/7;
}

.day16 {
  grid-column: 1/2;
}

.day17 {
  grid-column: 2/3;
}

.day18 {
  grid-column: 3/5;
}

.day19 {
  grid-column: 5/6;
}

.day20 {
  grid-column: 6/7;
}

.day21 {
  grid-column: 1/2;
}

.day22 {
  grid-column: 2/4;
}

.day23 {
  grid-column: 4/5;
}

.day24 {
  grid-column: 5/6;
}

.day25 {
  grid-column: 6/7;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" href="style.css">
</head>

<body>
  <div class="calendar">
    <div class="day day1">1</div>
    <div class="day day2">2</div>
    <div class="day day3">3</div>
    <div class="day day4">4</div>
    <div class="day day5">5</div>
    <div class="day day6">6</div>
    <div class="day day7">7</div>
    <div class="day day8">8</div>
    <div class="day day9">9</div>
    <div class="day day10">10</div>
    <div class="day day11">11</div>
    <div class="day day12">12</div>
    <div class="day day13">13</div>
    <div class="day day14">14</div>
    <div class="day day15">15</div>
    <div class="day day16">16</div>
    <div class="day day17">17</div>
    <div class="day day18">18</div>
    <div class="day day19">19</div>
    <div class="day day20">20</div>
    <div class="day day21">21</div>
    <div class="day day22">22</div>
    <div class="day day23">23</div>
    <div class="day day24">24</div>
    <div class="day day25">25</div>
  </div>
</body>

</html>
Laaouatni Anas
  • 4,199
  • 2
  • 7
  • 26
  • 2
    Your items have the same height. Yes, this is possible, however the question is about items with different widths and heights. – Azu Dec 25 '21 at 16:27
  • this need some changes with `grid-row` also (I assume I have to use this also) – Laaouatni Anas Dec 25 '21 at 16:37
  • @Azu i find a solution! use margin, and negative margins (is not the best solution, but it work fine) – Laaouatni Anas Dec 25 '21 at 16:49
  • Yes, this is possible if you align every item manually. But if you have the items loaded dynamically, this approach doesn't work. Anyway, thank you for trying. – Azu Dec 25 '21 at 17:01
  • @Azu I used `vh` for the margin, is like good in all dimensions (new modified answer), but like you said i can be dont working – Laaouatni Anas Dec 25 '21 at 17:04
  • @LaaouatniAnas sorry, but it would be very hard to use a grid to get the same aspect of the drawing. Look at the number 6 and number 11 widths, for example. Also look at number 10's height, compared to number 9's. It would take a grid of 50x50 to simulate all these aspects. – Jonathas Costa Dec 25 '21 at 18:01
  • 1
    @JonathasCosta yes, this is because is called Complex Grid Layout, I tried to do my best to help you, sorry if not good enough :( – Laaouatni Anas Dec 25 '21 at 18:05
  • Don't need to apologize, it was a good try though – Jonathas Costa Dec 25 '21 at 18:12
1

You can still keep your flex structure since you are using flex-wrap: wrap. The important thing is keeping total 100% width for each row which includes 5 box. If you want to give different height, you can just modify css code of box

.day {
  color: white;
  margin: 1%;
}

.calendar {
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
  width: 100%;
  height: 586px;
}

.day1 {
  width: 28%;
  height: 200px;
  background: lightblue;
}

.day2 {
  width: 8%;
  height: 200px;
  background: orange;
}

.day3 {
  width: 28%;
  height: 200px;
  background: lightcoral;
}

.day4 {
  width: 8%;
  height: 200px;
  background: lightgray;
}

.day5 {
  width: 18%;
  height: 200px;
  background: lightblue;
}

.day6 {
  width: 18%;
  height: 200px;
  background: lightsteelblue;
}

.day7 {
  width: 18%;
  height: 200px;
  background: lightblue;
}

.day8 {
  width: 18%;
  height: 200px;
  background: lightsteelblue;
}

.day9 {
  width: 18%;
  height: 200px;
  background: orange;
}

.day10 {
  width: 18%;
  height: 200px;
  background: cyan;
}

.day11 {
  width: 38%;
  height: 200px;
  background: lightcoral;
}

.day12 {
  width: 8%;
  height: 200px;
  background: lightgray;
}

.day13 {
  width: 18%;
  height: 200px;
  background: lightcyan;
}

.day14 {
  width: 8%;
  height: 200px;
  background: orange;
}

.day15 {
  width: 18%;
  height: 200px;
  background: lightcoral;
}
<div class="calendar">
  <div class="day day1">1</div>
  <div class="day day2">2</div>
  <div class="day day3">3</div>
  <div class="day day4">4</div>
  <div class="day day5">5</div>
  <div class="day day6">6</div>
  <div class="day day7">7</div>
  <div class="day day8">8</div>
  <div class="day day9">9</div>
  <div class="day day10">10</div>
  <div class="day day11">11</div>
  <div class="day day12">12</div>
  <div class="day day13">13</div>
  <div class="day day14">14</div>
  <div class="day day15">15</div>
</div>
Evren
  • 4,147
  • 1
  • 9
  • 16
1

The "right" answer

No. Wrapped flex items are spread on single (main) axis only. There is no automated mechanism to tell wrapped item that it's sibling on secondary axis in previous or following run (row above or below) overlaps some boundary and so should affect items in different row. Since your design involves overlaps on both axes, there is no way to define items' dimensions / transforms and let flex-box layout alone do the maths to produce desired balanced distribution.

Technically correct answer

Yes. But only with precisely "hand-crafted" styles.

As stated above, you would have to manually set extra properties to all "off-grid" overlaps on secondary axis. That means for secondary that each "expanded" item in one row must have manually "condensed" counterpart in adjacent row. Naturally, this could work only when wrapping occurs precisely as designed.

Let's have a look at simplified design sample:

aaaaaaa..b..cccc
aaaaaaa..b..cccc
aaaaaaa..b..cccc
............cccc
dddd..eeee..cccc
......eeee......
gggg..eeee..ffff
gggg............
gggg..h..iiiiiii
gggg..h..iiiiiii
gggg..h..iiiiiii

a and i areas are expanded horizontally at the expenses of areas h and b. Same applies to c-f and d-g pairs, just vertically. As we can see, overlaps happens on both axes. Let's say we need each of the four overlaps to have unique size.

POC with use of custom properties for setting width/height "overlap" and corresponding secondary axis (vertical) adjustments could be:

section {
 --base: 9em; /* width & height */
 --add: calc(var(--base) / 8); /* "unit" for overlap adjustments */
 --gap: calc(var(--base) / 20);
 --cols: 3; /* example HTML works with 3 only */
 --dim: calc( ( var(--cols) * var(--base) ) + ( ( var(--cols) - 1 ) * var(--gap) ) );
 width: var(--dim);
 height: var(--dim);
 gap: var(--gap);

 display: flex;
 flex-direction: row;
 flex-wrap: wrap;
}
article {
 width: calc( var(--base) + ( var(--add) * var(--wdt, 0) ) );
 margin-bottom: calc( var(--add) * var(--hgh,0) * -1 ); /* expanded items will "pull" next row back */
 position: relative;
 top: calc( var(--add) * var(--top,0) * 1 );
 height: calc( var(--base) + ( var(--add) * var(--hgh,0) ) );
}

/*
Illustrative
*/
section {
 outline: #F0F6 solid; outline-offset: -2px;
}
article {
 outline: #0FF6 solid; outline-offset: -2px;
 background-color: rgba(0,0,0,0.2);
 align-items: center;
 display: flex;
 justify-content: center;
 flex-direction: column;
 counter-increment: a;
 word-break: break-word;
 text-align: center;
}
article::before { content: counter(a, lower-alpha); }
article::after { content: attr(style); }
:root { background: dimgray; color: snow; }
:link { color: aqua; } :visited { color: lime; }
<section>
 <article style="--wdt: +1;"></article>
 <article style="--wdt: -1;"></article>
 <article style="--hgh: +2;"></article>

 <article style="--hgh: -4"></article>
 <article style="/* defaults */"></article>
 <article style="--hgh: -2; --top: +2"></article>

 <article style="--hgh: +4; --top: -4;"></article>
 <article style="--wdt: -3;"></article>
 <article style="--wdt: +3;"></article>
</section>

This have quite nice flexibility to set any value as overlap.

"Use grid" they say

Grid layout for our just fairly complex sample design with "3 × 3" base would involve at least 5 × 5 grid definition:

section {
 grid-template: 
"a a b b c" 60fr
"d e e e c" 10fr
"d e e e f" 20fr
"g e e e f" 30fr
"g h h i i" 60fr
/60fr
   05fr
     20fr
       35fr
         60fr;
 --base: 9em; /* item width & height */
 --gap: calc(var(--base) / 20);
 --cols: 3; /* example HTML works with 3 only */
 --dim: calc( ( var(--cols) * var(--base) ) + ( ( var(--cols) - 1 ) * var(--gap) ) );
 width: var(--dim);
 height: var(--dim);
 gap: var(--gap);
 outline: #F0F6 solid; outline-offset: -2px;
 display: grid;
}

/*
Illustrative
*/
article {
 outline: #0FF6 solid;
 outline-offset: -2px;
 background-color: rgba(0,0,0,.3);
 align-items: center;
 display: flex;
 justify-content: center;
 flex-direction: column;
 counter-increment: a;
 word-break: break-word;
 text-align: center;
}
article::before {
 content: counter(a, lower-alpha);
}
article::after {
 content: attr(style);
}
:root { background: dimgray; color: snow; }
:link { color: aqua; } :visited { color: lime; }
<section class="grid">
 <article style="grid-area: a"></article>
 <article style="grid-area: b"></article>
 <article style="grid-area: c;"></article>

 <article style="grid-area: d;"></article>
 <article style="grid-area: e;"></article>
 <article style="grid-area: f;"></article>

 <article style="grid-area: g;"></article>
 <article style="grid-area: h;"></article>
 <article style="grid-area: i;"></article>
</section>

Disadvantage of this approach is that each instance of ovelap on column-row pair demands extra column-row definition. Concrete design from the question (5 × 5) would need at least 19 × 17 grid definition.


Please note that both POC's are synthetic and involves quite modern gap for flex-box. Real-world usage would most probably be more complicated that this.


Elegant grid approach

Simple rigid grid and irregular adjustments done with only margins.

This approach is taken from other answer of this question, all kudos there.

I didn't know that margin of grid items could do that; adding example just for completeness. Using logic similar to the first flex-box example:

section {
 --base: 9em; /* item width & height */
 --add: calc(var(--base) / 8); /* "unit" for overlap adjustments */
 --gap: calc(var(--base) / 20);
 --cols: 3; /* example HTML works with 3 only */
 --dim: calc( ( var(--cols) * var(--base) ) + ( ( var(--cols) - 1 ) * var(--gap) ) );
 width: var(--dim);
 height: var(--dim);
 gap: var(--gap);
 display: grid;
 grid-auto-rows: 1fr;
 grid-template-columns: repeat(var(--cols), 1fr);
}
article {
 margin-top: calc( var(--top,0) * var(--add) * -1 );
 margin-right: calc( var(--right,0) * var(--add) * -1 );
 margin-bottom: calc( var(--bottom,0) * var(--add) * -1 );
 margin-left: calc( var(--left,0) * var(--add) * -1 );
}
/*
Illustrative
*/
section {
 outline: #F0F6 solid; outline-offset: -2px;
}
article {
 outline: #0FF6 solid;
 outline-offset: -2px;
 background-color: rgba(0,0,0,.3);
 align-items: center;
 display: flex;
 justify-content: center;
 flex-direction: column;
 counter-increment: a;
 word-break: break-word;
 text-align: center;
}
article::before {
 content: counter(a, lower-alpha);
}
article::after {
 content: attr(style);
}
:root { background: dimgray; color: snow; }
:link { color: aqua; } :visited { color: lime; }
<section>
 <article style="--right: +1;"></article>
 <article style="--left: -1;"></article>
 <article style="--bottom: +2;"></article>

 <article style="--bottom: -4"></article>
 <article style="/* default */"></article>
 <article style="--top: -2"></article>

 <article style="--top: +4;"></article>
 <article style="--right: -3;"></article>
 <article style="--left: +3;"></article>
</section>

I see this as the most elegant and versatile way and nice leveraging of rigid grid for design. Manual "number lifting" would be still daunting - especially when need arises to shift "rest" of the row or even worse whole 'table' - but even so it seems as the most intuitive. Again, kudos to Laaouatni Anas for bringing it here.


Pragmatic old-school approach: "abspos"

For really wild rigid irregular design consider absolute positioning. Shown flex-box and first grid approach might make sense for usage when there are some regularities that could be accommodated in code patterns. Otherwise it might be equally viable approach to either use simple absolute positioning and manual dimensions for everything or similar approach with second grid. Sounds terrible - repositioning items further down or right would be pain - but I would not be surprised if would prove as the best solution for this particular task.


myf
  • 9,874
  • 2
  • 37
  • 49