Making a honeycomb pattern in Javascript and Scss

Mark Lyck
4 min readDec 23, 2016

Tools used: React, Javascript, JQuery, Lodash

I recently redid my portfolio site http://markdid.it and I wanted to do something a little bit different for how to showcase the skills and libraries I use to build websites. I came up with this concept:

A honeycomb pattern of the things I use in development. However it had to be responsive and there was no tutorials on how to do this online. So I sat down and thought about it. There are several problems to overcome.

  • Every other line, needs to have one less item in it.
  • They need to be centered. Unless the last line of items and the above line of item have either both an equal or unequal length of items.
  • In case that the above and last line have an equal or unequal length, the line needs to be shifted the size of 1 item + margin.

There may be a better solution to this problem. But to solve the above requirements, I used Javascript with JQuery.

I decided to make each line a list, with list items in it, so the result would look something like this:

<div class="lists-container">
<ul>
<li></li>
<li></li>
<li></li>
</ul>
<ul>
<li></li>
<li></li>
</ul>
<ul>
<li></li>
<li></li>
<li></li>
</ul>
</div>

First I define the following:

  • Width of a single item.
  • The margin between the items and the side.
  • The width of the window
const skillWidth = 111
const margin = 80
const windowWidth = $(window).width()

I use these to calculate how many skills can fit in one line

const maxSkillsInOneList = Math.floor(((windowWidth - (margin * 2)) / skillWidth))

I created my list of skills. Here’s an example:

const skills = [
{
name: 'HTML',
img: require('./skills_images/html.png')
}
]

I then map over my list of skills, to create JSX elements

let skillsetItems = skills.map((skill, i) => {
return (<li key={i} className="skill"
data-name={skill.name}>
<img src={skill.img} alt={skill.name}/>
</li>)
})

I made an empty array for all of the lists

let skillLists = []

Then I figured out the number of lists needed from the constants we found earlier.

let numberOfLists = Math.round(skillsetItems.length / (maxSkillsInOneList - 0.5))

adding -0.5 ensures there’ll never be 1 list short, as every other list, is going to have 1 item less in it.

Then we can add the correct number of lists to our skillLists

while (skillLists.length < numberOfLists) { skillLists.push('list') }

What we put in each item in the array, doesn’t matter as we’ll be mapping over it almost immediately.

But first we need to set some outside variables to use in the map function.

let currentSkill = 0let numberOfSkillsInLastList = 0
let numberOfSkillsInThisList = 0

In this map function we are going to put all the skills that can fit in the current list “i” into the list, then move on to the next list until we’ve filled out the lists.

skillLists = skillLists.map((skillList, i) => {
numberOfSkillsInThisList = 0
let skillsInList = []

First we reset the skillsInThisList variable to 0. And we start a new empty array for skills in the list.

Then we figure out how many skills can fit in the current list we’re looping through.

If it’s an equal number, we add as many skills as there can be in one list, using the constant we defined earlier “maxSkillsInOneList”, if it’s unequal we remove 1 from that number.

We then add as many skills to the list as there can fit.

We also add 1 to our currentSkill counter, and if the currentSkill exists we add 1 to numberOfSkillsInThisList. For the last list the currentSkill will be ‘undefined’ for each item until it’s full, if it doesn’t equal the maxNumberOfSkillsInThisList.

let maxNumberOfSkillsInThisList = maxSkillsInOneList
if (i % 2 !== 0) { maxNumberOfSkillsInThisList-- }
let skillsInList = []
while (skillsInList.length <= maxNumberOfSkillsInThisList) {
skillsInList.push(skillsetItems[currentSkill])
if (skillsetItems[currentSkill]) {
numberOfSkillsInThisList++
}
currentSkill++
}

We can then remove all the undefined. I used Lodash for this, as I already have it in my project.

skillsInList = _.without(skillsInList, undefined)

Everything except the last line is function. In the edge cases where the last line and the line before it however both have equal or unequal length. The last line will be positioned incorrectly.

But we can easily take care of this with an if statement and a forced margin.

if ((numberOfSkillsInLastList % 2 === 0 
&& numberOfSkillsInThisList % 2 === 0)
|| (numberOfSkillsInLastList % 2 !== 0
&& numberOfSkillsInThisList % 2 !== 0)) {
if (i === skillLists.length - 1) {
return (<ul className="skill-list"
key={i}
style={{marginRight: `${skillWidth}px`}}>
{skillsInList}
</ul>)
}
}

Lastly we can finish the map function with returning our lists of lists and setting numberOfSkillsInLastList

numberOfSkillsInLastList = numberOfSkillsInThisList
return (<ul className="skill-list" key={i}>{skillsInList}</ul>)

And we finally met all our criteria except design.

Here’s the Scsss:

.skills-container {
.skill-list {
display: flex;
flex-wrap: wrap;
justify-content: center;
.skill {
width: 100px;
margin: 0 5px -20px 5px;
img {
width: 100px;
}
}
}
}

as the end result, you’ll see something like what I have on my website: http://markdid.it/

I made the hexagon images with Figma, so you’ll have to adjust the bottom margin, to match the size of your images, if -20px doesn’t fit perfectly.

The only things I didn’t cover here, is making the items smaller on small devices, and handling the hover effects. If you’d like to see more, let me know!

P.S There’s definitely room for improvements here, and I’m always interested in hearing about better ways to do things.

If you want to see the full code, here’s my repository for the ‘skills component’: https://github.com/MarkLyck/Portfolio-v2.0/tree/master/src/components/Toolbelt

--

--