Bento Layouts Tilt Me
I was recently playing around with bento layouts (if you don’t know what Bento layout / grid is, it’s the new trend all the cool kids are doing, catch up) and actually automating it a little - basically using grid-auto-flow on grid layout. Then I got an idea: “Could I make the tiles tilt towards the mouse?” Why? - Why not? Let’s do it.
Bento layouts actually don’t tilt me, sorry. I just wanted to use this awesome clickbait opportunity for what I’ve made. This is a semi-demo, semi-tutorial, short post about how to do it.
Making a Single Tile Tilt
Tilting a single tile is actually pretty simple and a really nice person - some Armando Canals, already did that.
All I needed to do was scale it by adding multiple tiles, inside a grid layout. The code is here, with the relevant parts described in the comments:
<style>
body {
background: #302c31;
}
.container {
height: 100%;
width: 100%;
/* Make it a grid */
display: grid;
/* 8 columns and 4 rows */
grid-template-columns: repeat(8, 100px);
grid-template-rows: repeat(4, 100px);
/* Make the layout dense = fill out any "holes" and leave uncompleted rows if necessary */
/* Try commenting the dense option out to see the difference */
grid-auto-flow: dense;
align-content: center;
justify-content: center;
gap: 8px;
}
.box {
background: white;
}
/* Here we make some if the tiles different size */
/* to see the effect of the grid layout */
.box:is(:nth-child(2), :nth-child(8), :nth-child(10)) {
grid-column: span 2;
grid-row: span 1;
}
.box:is(:nth-child(3), :nth-child(7), :nth-child(13)) {
grid-column: span 2;
grid-row: span 2;
}
</style>
<body>
<!-- Spawn multiple boxes -->
<div class="container" id="container">
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</div>
</body>
<script>
// Try smaller numbers to see more tilt
let constrain = 30;
// the part where mouse movement will be taken into account
// replace with 'body' to consider the whole body of the webpage
let mouseOverContainer = document.getElementById("container");
// select all our boxes
let boxes = document.getElementsByClassName("box");
function transforms(x, y, el) {
let box = el.getBoundingClientRect();
let calcX = -(y - box.y - (box.height / 2)) / constrain;
let calcY = (x - box.x - (box.width / 2)) / constrain;
// try smaller perspective for more tilt. (may produce some fun artifacts)
return "perspective(500px) "
+ " rotateX("+ calcX +"deg) "
+ " rotateY("+ calcY +"deg) ";
}
function transformElement(el, xyEl) {
el.style.transform = transforms.apply(null, xyEl);
}
mouseOverContainer.onmousemove = function(e) {
let xy = [e.clientX, e.clientY];
window.requestAnimationFrame(function(){
// Calculate the transform for all our boxes
for (const box of boxes) {
let position = xy.concat([box]);
transformElement(box, position);
}
});
};
</script>
Bonus: Making the Highlight Pop
We can add some more flavour to the layout - the hovered tile can pop out. To do that, we check whether we’ve hovered a smaller area inside the tile, and if so, we scale it. Simple and effective. To try it out, just replace your transforms function with the one below.
function transforms(x, y, el) {
let box = el.getBoundingClientRect();
let calcX = -(y - box.y - (box.height / 2)) / constrain;
let calcY = (x - box.x - (box.width / 2)) / constrain;
// try smaller perspective for more tilt. (may produce some fun artifacts)
let transformString = "perspective(500px) "
+ " rotateX("+ calcX +"deg) "
+ " rotateY("+ calcY +"deg) ";
// make the hover area half the tile's bounding box
const halfBox = {
left: box.left + box.width / 4,
right: box.right - box.width / 4,
top: box.top + box.height / 4,
bottom: box.bottom - box.height / 4,
}
// if the mouse is within the half-BBox,
// don't apply the rotation, but scale the tile instead
if (x >= halfBox.left && x <= halfBox.right
&& y >= halfBox.top && y <= halfBox.bottom) {
transformString = " scale(2) "
}
return transformString;
}
The End
Thanks for reading this quick post. Now that you are a cool kid, tweet (X?) me your best Bento grid @devslovecoffee.
Don’t forget to check out my other posts and projects ☕.