In this post, you'll learn how to create a honeycomb card with HTML, CSS, and JavaScript from this codepen: https://codepen.io/Joanverhulst/pen/OJBbbbd.
Part of this code like the Magical Hover effect, is from https://codepen.io/Hyperplexed/pen/MWQeYLW which I took a lot of inspiration from.
Lets jump into the code 🤓
The HTML code creates a section with a class of "content".
In this section, we create a "div" element with an "id" of "cards" and a class of "cards". This div element contains another "div" element with a class of "card", which is the main card element.
Inside the "card" element, we have a "div" element with a class of "card__content", which contains the title and description of the card. We also have a "div" element with a class of "honeycomb", which is where the honeycomb pattern is generated using JavaScript.
This is what the html looks like:
<section class="content">
<div id="cards" class="cards">
<div class="card">
<div class="card__content">
<h4 class="card__title">Starter Tier.</h4>
<p class="card__description">This tier is perfect for those who are just getting started. You'll have access to all the essential features, including a simple interface, basic analytics, and email support.</p>
</div>
<div class="honeycomb">
<!-- Contains hexagon rows and hexagon cells generated with javascript -->
</div>
</div>
</div>
</section>
Next up, the necessary JavaScript code:
The first section of code targets the container for the cards and defines two variables, numCells and numRows. These two variables are used to calculate the number of hexagons that should be displayed in the honeycomb pattern for each card.
const cardsContainer = document.getElementById("cards");
let numCells, numRows;
Next we will create a function function that calculates the number of rows and cells that should be used for each card based on the size of the container element.
This function is called once at the beginning of the script and again every time the window is resized.
We use Math.ceil to round up the number, this way there will always be a bit more cells and rows so it can fill the entire card.
function calculateGridSize() {
const rect = cardsContainer.getBoundingClientRect();
numCells = Math.ceil(rect.width / 32);
numRows = Math.ceil(rect.height / 32);
}
calculateGridSize();
Next we will create a function that generates the HTML for the honeycomb pattern for each card, based on the number of rows and cells calculated earlier. The function first selects the honeycomb div within the card element and then generates the HTML for the hexagon rows and cells using a for loop.
function generateHexagons(card) {
const honeycomb = card.querySelector(".honeycomb");
let html = "";
for (let i = 0; i < numRows; i++) {
html += `<div class="hexagon-row">`;
for (let j = 0; j < numCells; j++) {
html += `<div class="hexagon"></div>`;
}
html += `</div>`;
}
honeycomb.innerHTML = html;
}
Finally, the code at the end of the script tracks the position of the mouse cursor over the cards and updates the CSS variables --mouse-x and --mouse-y for each card element. These variables are used in the SCSS code to create the shine effect on hover. This idea is from Hyperplexed, which I have to give full credit; https://codepen.io/Hyperplexed/pen/MWQeYLW.
document.getElementById("cards").onmousemove = (e) => {
for (const card of document.getElementsByClassName("card")) {
const rect = card.getBoundingClientRect(),
x = e.clientX - rect.left,
y = e.clientY - rect.top;
card.style.setProperty("--mouse-x", `${x}px`);
card.style.setProperty("--mouse-y", `${y}px`);
}
};
Finally let's give it some styling.
First, we will add some css variables and a scss variable for a timing function which we will use in the project.
@import url("https://fonts.googleapis.com/css2?family=Hanken+Grotesk:wght@400;500;600&display=swap");
// SCSS variable for timing function.
$ease: cubic-bezier(0.3, 0.8, 0.5, 0.5);
// CSS variable for color.
:root {
--primary: hsla(5, 90%, 30%, 1);
--neutral-50: hsla(215, 5%, 98%, 1);
--neutral-100: hsla(215, 5%, 72%, 1);
--neutral-200: hsla(215, 5%, 36%, 1);
--neutral-300: hsla(215, 5%, 24%, 1);
--neutral-400: hsla(215, 5%, 20%, 1);
--neutral-500: hsla(215, 5%, 16%, 1);
--neutral-600: hsla(215, 5%, 12%, 1);
--neutral-700: hsla(215, 5%, 8%, 1);
--neutral-800: hsla(215, 5%, 6%, 1);
--neutral-900: hsla(215, 5%, 3%, 1);
}
* {
font-family: "Hanken Grotesk", sans-serif;
box-sizing: border-box;
margin: 0;
padding: 0;
}
First, lets style our hexagon cells and honeycomb pattern,
To create a hexagon shape, we can use clip-path.
The clip-path property creates a hexagon shape by defining six points using percentages. The first point is at the top center (50% 0%), the next two points are on the right edge (100% 25% and 100% 75%), the fourth point is at the bottom center (50% 100%), and the last two points are on the left edge (1% 75% and 1% 25%). The slight margin between cells is created by adjusting the position of the outermost points (1% instead of 0% and 99% instead of 100%).
Then we target every other row of hexagons by using the :nth-child pseudo selector and only targeting the even numbers, we offset the X value by 50% so we get a honeycomb pattern.
.hexagon {
min-height: 32px;
min-width: 32px;
background: var(--neutral-800);
clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 1% 75%, 1% 25%);
transition: all 0.5s $ease;
}
.hexagon-row {
display: flex;
width: 100%;
margin-bottom: -7px;
}
.hexagon-row:nth-child(even) .hexagon {
transform: translateX(50%);
}
.honeycomb {
transform: translate(-1rem, -1rem);
}
Next up, lets add some default styling to the card:
We give the card a position relative, with a height of 256px,
you can set a fixed width, but the width of this card will be controlled by a grid,
then we add a border and set a border-radius. We add overflow hidden so the hexagon pattern is contained within the card.
Last but not least we add a box-shadow to create an outline for the card, this will appear visible when hovered by changing the color of the shadow.
On click there is a slight scale down, by using transform: scale(0.98);
Then we place the content with position absolute.
.card {
position: relative;
height: 256px;
border-radius: 0.5rem;
border: thin solid var(--neutral-600);
transition: $ease all 0.2s;
overflow: hidden;
cursor: pointer;
box-shadow: 0px 0px 0px 3px var(--neutral-800);
&:hover {
box-shadow: 0px 0px 0px 3px var(--neutral-700);
}
&:active {
transform: scale(0.98);
}
&__content {
position: absolute;
z-index: 1;
color: white;
padding: 2rem;
}
&__title {
margin-bottom: 1rem;
}
&__description {
color: var(--neutral-200);
}
}
Finally we will add the glow effect by creating a "shine-effect" mixin, a mixin can be used all trough the project or stylesheet.
@mixin shine-effect($size, $color) {
&::before {
position: absolute;
border-radius: inherit;
content: "";
width: 100%;
height: 100%;
top: 0;
left: 0;
opacity: 0;
background: radial-gradient(
$size circle at var(--mouse-x) var(--mouse-y),
$color 10%,
transparent 50%
);
filter: blur(2rem);
transition: all 1s $ease;
}
&:hover::before {
opacity: 1;
}
}
First we will create a ::before pseudo-element that will be added to the card. We set the opacity to 0 so it isn't visible when not hovering over it. Next up we create a radial gradient that follows the mouse position we set earlier with JS. We give it two parameters, $size and $color, that we can set when the mixin gets used.
Finally we set the hover of the before to opacity, to make the effect visible when hovering.
Finally we need to include the mixin to the card and that concludes business.
.card {
//other card code
@include shine-effect(96px, var(--primary));
//...
}
If you want to view the full working code with additional cards and styling, here is a codepen:
Top comments (0)