Building a cursor-responsive grid.
A quiet background that comes to life as you move. Here’s how the grid behind this portfolio works, from a few CSS lines to a little cursor tracking.
A portfolio is mostly words and links. I wanted the background to give it a little depth without getting in the way of either. The result is a grid that stays faint until the cursor moves nearby: cells lift, their edges catch the light, and a few technology logos appear.
The useful constraint is that the page still needs to work when none of that happens. The writing comes first. The effect is an extra layer.
Start with CSS
The resting grid is just two perpendicular gradients. Each draws a one-pixel line, then leaves the rest of the cell transparent. Repeating them every 48 pixels gives the page its square pattern.
.grid-page {
background-image:
linear-gradient(#ffffff0a 1px, transparent 1px),
linear-gradient(90deg, #ffffff0a 1px, transparent 1px);
background-size: 48px 48px;
}
This also serves as the fallback. If JavaScript is unavailable, there’s still a complete page with the same visual rhythm.
Follow the cursor
For the interactive layer, each cell gets a value called --lift. It ranges from zero to one, based on the distance between the cell’s center and the cursor. Nearby cells rise; distant cells stay flat.
const distance = Math.hypot(x - cell.x, y - cell.y); const lift = Math.max(0, 1 - distance / 180) ** 2;
cell.element.style.setProperty(‘—lift’, lift.toFixed(3));
Squaring the value makes the effect fall away more quickly at the edges. CSS uses that single value for the upward translation, the highlighted border, and the shadow beneath each cell.
Pointer movement schedules an update with requestAnimationFrame. Several events can arrive before the browser draws again, so they share one pending update. Scrolling also updates the cursor’s position relative to the page.
One proximity value is enough to make the lift, light, and logos feel like parts of the same effect.
Keep it quiet
The visual effect works best when most of the page stays still. A few choices keep it in the background:
- Leave space between logos. They only appear on a sparse selection of cells, with at least one empty cell between them.
- Keep the resting lines faint. The contrast increases near the cursor, where it has a reason to be visible.
- Respect the reader’s preferences. Touch input and reduced-motion settings leave the grid still.
That’s the whole idea: a small detail that rewards curiosity while leaving the content easy to read.