6

I'm trying to create some sort of heat map for the network I'm working on. I wanted to use a beehive for this as it looks fancy and gives me great space utilization.

if found this website where i figured out how to use hexagons with pure CSS. but it was heavily reliant rows and offsets. my entire UI is KnockoutJS-driven and has dynamic number of PCs on the network at any given time. (mostly docker containers going up or down).

Clusters can vary between 1 and n+1 nodes.

I looked at this website: CSS Hexagon and found several solutions to manage the hexagons but all of them are REALLY limited in their dynamic use.

This is the intended code:

 <div class="panel-body>
      {{noescape "<!-- ko foreach: { data: vm.dash().nodes, as: 'node' } -->" }}
          <span class="flex-span" data-bind="attr: { 'id': node.id }, css: { up: node.Status == 2, down: node.Status != 2 }">&#x2B22;</span>
      {{noescape "<!-- /ko -->"}}
 </div>

Based on that it would give the hexagon a color that would indicate up / down

I've whipped up a non knockout fiddle with my flexbox idea, but I don't truly understand flexbox that well so it's obviously not working: Fiddle

#container {
  max-width:400px;
  max-height:400px;
}

.hex:before {
    content: " ";
    width: 0; height: 0;
    border-bottom: 30px solid #6C6;
    border-left: 52px solid transparent;
    border-right: 52px solid transparent;
    position: absolute;
    top: -30px;
    flex-shrink: 1;
    flex-grow:1;
    flex-basis: 130px;
}

.hex {
    margin-top: 30px;
    width: 104px;
    height: 60px;
    background-color: #6C6;
    position: relative;
    float:left;
    margin-bottom:30px;
    flex-shrink: 1;
    flex-grow:1;
    flex-basis: 130px;
}

.hex:after {
    content: "";
    width: 0;
    position: absolute;
    bottom: -30px;
    border-top: 30px solid #6C6;
    border-left: 52px solid transparent;
    border-right: 52px solid transparent;
    flex-shrink: 1;
    flex-grow:1;
    flex-basis: 130px;
}
<div id="container">
  <div class=hex></div>
  <div class=hex></div>
  <div class=hex></div>
  <div class=hex></div>
  <div class=hex></div>
  <div class=hex></div>
  <div class=hex></div>
  <div class=hex></div>
  <div class=hex></div>
  <div class=hex></div>
</div>

The problems I'm running into are:

  1. How do I scale these divs dynamically regardless of number, and occupying maximum space.
  2. How do I use a pure CSS solution to make sure that all even "rows" are offset by half a hexagon.

!! UPDATE !!

So i've added the concept of pre-determined rows: fiddle I'm still looking for a way to make the row offset depend on the ".hex" class width. and have the hex class scale with the amount of elements. but i think the grid itself looks really good now.

Puzzle84
  • 540
  • 3
  • 20
  • ideally i'd use something like ::even-line but i could only find ::first-line to do the offsets for the hexagons. – Puzzle84 Jan 10 '17 at 19:15
  • If you're already using knockout why do you need a pure CSS solution? I would recommend making a knockout component and binding your css attributes to computed properties that calculate your scaling based on the number of nodes. – Jason Spake Jan 10 '17 at 19:46
  • No real reason, i just imagined css being a lot nicer to the ui loading times. And how would you go about doing the even / odd line logic if your ui is responsive? – Puzzle84 Jan 10 '17 at 19:50
  • 1
    Is this what you tried to do ? https://jsfiddle.net/ndy71xos/1/ ( `:nth-child(xn)`works with line of 3) . to make hexagons grow/shrink when size change, do not use pixels but eventually % .Flex won't be any help to resize height of .hex nor the pseudo elements – G-Cyrillus Jan 10 '17 at 20:19
  • That's exactly what i wanted GCyrillus, as for the % that's fine but i guess i'll have to set some rules about when to make the % smaller? can this be done automatically let's say i set it to 33% so i have 3 in a row. does CSS know when the rows have passed the boundaries of the parent? at which the % should shrink to let's say 25%? – Puzzle84 Jan 10 '17 at 20:34
  • And the bottom hexagon would have to stay inside the parent boundaries – Puzzle84 Jan 10 '17 at 20:43
  • This should help : [CSS responsive grid of hexagons](http://stackoverflow.com/questions/26114920/responsive-grid-of-hexagons) and it also has a [github repo](https://github.com/web-tiki/responsive-grid-of-hexagons) – web-tiki Jan 11 '17 at 08:07
  • It's responsive in the sense that the grid resizes if the container resizes. My question is the opposite. i need the items to change if the number of items changes. so once the max number of tiles that fit inside the box at current size exceeds, change the size of the hexagons so that another / column appears. and continue untill previous issue happens again. decrease the size and continue etc.. – Puzzle84 Jan 11 '17 at 17:45

2 Answers2

2

So if you need 3 hexagons per row :nth-child can help to set the margin once every 2 rows.

To size your elements, you may use percentage, but you will have to use pseudo without borders , but a background and a rotation instead.

example:

#container {
  max-width: 400px;
  max-height: 400px;
  border: solid;padding:1px;
}
/* demo*/
#container:hover {

  max-width: 600px;
  max-height: 600px;
 }/* */
.hex {
  margin-top: 8%;
  width: 28%;
  padding: 8% 0;
  background-color: #6C6;
  position: relative;
  margin-bottom: 3px;
  margin-right: 0px;
  display: inline-flex;
  position: relative;
  align-items: center;
  justify-content: center;
}
.hex:after,
.hex:before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0%;
  background: inherit;
  transform: rotate(60deg);
}
.hex:after {
  transform: rotate(-60deg)
}
.hex:nth-child(6n+1) {
  margin-left: 14%;
}
<div id="container">
  <div class=hex></div>
  <div class=hex></div>
  <div class=hex></div>
  <div class=hex></div>
  <div class=hex></div>
  <div class=hex></div>
  <div class=hex></div>
  <div class=hex></div>
  <div class=hex></div>
  <div class=hex></div>
</div>
G-Cyrillus
  • 101,410
  • 14
  • 105
  • 129
  • Almost perfect, but it does not scale down when more elements get added, to the container. it overflows instead of making the hexagons smaller. – Puzzle84 Jan 10 '17 at 20:53
  • @Puzzle84 I'm afraid this part won't be doable via CSS, for this kind of behavior, canvas and javascript will be more appropriate :( unless you can make a mix of vh & vw .... – G-Cyrillus Jan 10 '17 at 20:54
  • @vals it happens to me all the time :) – G-Cyrillus Jan 10 '17 at 20:55
  • Ack well i'll accept this answer as the winning one and see if i can figure it out somewhat gracefully with javascript then :) – Puzzle84 Jan 10 '17 at 20:56
  • https://jsfiddle.net/sdwttp31/1/ is what i want it to look like. trying to figure out a way to use the hex element to make the entire thing scale. – Puzzle84 Jan 10 '17 at 22:20
1

I don't think that it is posible to apply an style in a flex container based on the row number (even/odd).

So, I have set the hexagons overlapping in the same row, and ensured that in a row there will always be an even number of hexagons (the even ones don't have space requirement due to the negative margins, so they will always fit).

Here is my solution. Hover on the container to change the width.

#container {
  max-width: 410px;
  max-height: 400px;
  display: flex;
  flex-wrap: wrap;
  border: solid 1px green;
  transition: max-width 6s;
}
#container:hover {
  max-width: 800px;
}
.hex {
  margin-top: 30px;
  height: 60px;
  background-color: #6C6;
  position: relative;
  margin-bottom: 90px;
  flex-shrink: 0;
  flex-grow: 0;
  flex-basis: 104px;
}
.hex:nth-child(even) {
  background-color: blue;
  transform: translateY(90px);
  margin-left: -52px;
  margin-right: -52px;
}
.hex:before {
  content: " ";
  width: 0;
  height: 0;
  border-bottom: 30px solid #6c6;
  border-left: 52px solid transparent;
  border-right: 52px solid transparent;
  position: absolute;
  top: -30px;
  flex-shrink: 1;
  flex-grow: 1;
  flex-basis: 130px;
}
.hex:after {
  content: "";
  width: 0;
  position: absolute;
  bottom: -30px;
  border-top: 30px solid #6c6;
  border-left: 52px solid transparent;
  border-right: 52px solid transparent;
  flex-shrink: 1;
  flex-grow: 1;
  flex-basis: 130px;
}
.hex:nth-child(even):before {
  border-bottom-color: blue;
}
.hex:nth-child(even):after {
  border-top-color: blue;
}
<div id="container">
  <div class="hex"></div>
  <div class="hex"></div>
  <div class="hex"></div>
  <div class="hex"></div>
  <div class="hex"></div>
  <div class="hex"></div>
  <div class="hex"></div>
  <div class="hex"></div>
  <div class="hex"></div>
  <div class="hex"></div>
  <div class="hex"></div>
</div>
vals
  • 61,425
  • 11
  • 89
  • 138
  • Almost exactly what i want from a wrapping perspective. but there should be no restriction on green and blue to have the same amount of elements. Basically, the 2nd line of blues should be the remaining 2 greens in the line above – Puzzle84 Jan 10 '17 at 20:41