A Tangram Puzzle With No JavaScript? Sass Says Otherwise

Drag-and-drop games with rotation and spatial logic feel like JavaScript territory. But with Sass — CSS's preprocessor — you can replace much of the low-level logic you'd normally write in JS. Maps, mixins, functions, and math handle the heavy lifting. The result? A fully interactive Tangram puzzle that runs without a single line of JavaScript.

Why bother? It's a useful exercise in pushing CSS to its limits, sharpening your Sass skills, and having some fun along the way.

The Game Mechanics

The game uses the classic seven-piece Tangram set. Players must recreate a target silhouette — in this case, a camel. The build includes:

  • A task board displaying the target shape.
  • A start button that shuffles all pieces into a staging area.
  • Clickable, interactive pieces that can be rotated.
  • Feedback: incorrect placements are indicated, and puzzle completion triggers a visual celebration.

The HTML Structure

The markup is intentionally verbose because CSS needs specific hooks. Key decisions:

  • Radio buttons for piece selection. Each of the seven shapes gets its own radio group. Radios provide built-in exclusivity — only one can be selected per group — making it easy to track the active shape.
  • Radio buttons for rotation. Eight inputs represent 45-degree increments from 45° to 360°. Clicking cycles through them infinitely.
  • Radio buttons for shadow positions. Every possible drop location on the board has its own input.
  • A reset button. A classic <button type="reset"> inside a <form> allows players to start over.

Given the sheer number of elements, the HTML is generated with Pug for convenience. It doesn't affect puzzle logic — just keeps the markup manageable.

Storing Piece Data in Sass Maps

Sass maps work like JavaScript objects — pairs of keys and values that let you look up data efficiently. All core information for each tangram piece (tan) lives in maps:

  • background-color for each tan
  • clip-path coordinates defining the shapes
  • Initial positions
  • The blocking div position (disabled when a tan is active)
  • Shadow positions on the task board
  • Grid placement info
  • Winning combinations — exact target coordinates for the correct solution

A utility function dynamically adds units (like vmin or px) to numeric map values. This makes the maps reusable across different contexts without hard-coding units.

Mixins That Read the Maps

The first mixin assigns grid-column and grid-row placement using a map where each element index (1–8) holds four values: start column, end column, start row, end row.

Another mixin applies the actual shapes using clip-path. Rather than SVG images, the project uses CSS clipping — a simpler approach that keeps the focus on logic over visual polish. The shapes are cut from their initial square container using coordinates stored in the $tansShapes map, which also handles each tan's color.

Start and Rotation Logic

When the Start button is clicked, tans receive a transform: translate() with specific coordinates and rotation, moving them from the staging area to their scattered positions.

Piece rotation works through eight radio buttons. With each click, the active button disappears and the next one appears. At the last button, clicking wraps back to the first. All buttons share identical styling, giving the illusion of one continuously clickable rotation control that spins the tan around its center.

Rotation determines which shadow positions appear on the board. If no valid drop positions exist for the current angle, no shadows are displayed.

Shadow Placement and Win Detection

Shadow coordinates are stored statically in a map. When a player clicks a tan, its possible drop locations — rendered as shadows — become visible. Each shadow gets a unique ID combining the parent tan's name with a sequence number for the current rotation angle, avoiding naming complexity.

Clicking a shadow moves the corresponding tan to that position. The logic then checks the position against a map of valid winning combinations. Since some tans share dimensions — and certain arrangements can combine to form larger existing shapes — multiple correct solutions exist per puzzle. All are cataloged in the map.

  • Correct placement: The tan blinks briefly, becomes unclickable, and locks into place.
  • Incorrect placement: The tan stays at the shadow location but remains clickable, so the player can keep trying.

When every tan locks into its correct position, the camel silhouette merges and turns yellow — the completion signal. The only remaining interaction is the Restart button.

It's a long, winding build — but that's the price of taking the challenging route. The payoff is a fully functional puzzle game built entirely with (S)CSS, no JavaScript required.