Let's Solve Puzzles! – Frontend Masters Blog

Let’s Solve Puzzles! – Frontend Masters Blog

2 Min Read

Sometimes a standard rectangular card feels a bit too ordinary. A puzzle-piece shape can alter the vibe: it might make a gallery more fun, transform a loading state into a revelation, give a hero section a tactile essence, or enable you to divide an image into interactive parts that seem to belong together.

The exciting aspect is that we can create the entire setup using modern CSS with a pinch of JavaScript. We’ll begin with a single piece, make its shape configurable, then generate a complete puzzle where adjacent pieces have matching tabs and sockets. Eventually, we’ll insert actual content within the pieces, making the effect practical, not merely ornamental.

We’ll execute this in three stages:

  1. Sketch a single puzzle piece using clip-path: shape().
  2. Construct a dynamic grid of interconnected pieces with aligned edges.
  3. Embed actual content within each piece and align it accurately.

Step 1: Draw One Puzzle Piece

The preliminary target is to outline the silhouette of a puzzle piece solely using CSS. To achieve that, we must decompose the shape into its geometrical elements. Essentially, a puzzle piece is a square with four edges, and each edge can be either flat, protrude, or indent. These protrusions and indentations are known as “tabs” and “sockets”, and they can be positioned anywhere along the edge.

An image of a puzzle piece with labeled sections: 'Socket' pointing to the inward curve, 'Tab' indicating the protruding part, and 'Flat side' highlighting the straight edge, all set against a dark background.
Image of a puzzle piece with labeled edges and tabs/sockets

In the picture above, the outer square with the dashed pink outline signifies the overall size of the piece, and the tabs are sized to be 20% of the piece size. This sets the inner square to be 60% of the piece size, simplifying the math. Let’s convert that into code.

<div class="puzzle-piece"></div>Code language: HTML, XML (xml)

We’ll commence with a singular .puzzle-piece element, utilizing clip-path: shape() for shaping. The shape function facilitates path definition with an amalgam of straight lines and curves, ideal for this organical shape and allowing the use of relative coordinates with custom properties for requisite flexibility.

The shape will be applied to a pseudo-element, simplifying styling and positioning of the base element. This allows us to hone the geometry without the interference of content or layout considerations.

.puzzle-piece {
position: relative;
width: 160px;
aspect-ratio:

You might also like