Slide Puzzle

Sliding Tile Background for React

Icon tiles in a faint grid of slots around your headline, moving like a sliding puzzle. The first time it scrolls into view the tiles start scrambled and the puzzle solves itself, one cell at a time; after that a tile slides into a free slot every few seconds. Sweep the mouse fast into a tile and it's shoved along the grid with the tiles in front of it; rest on one and its name rises above it.

  • Background
  • Interaction
  • Landing

Uses clsx, tailwind-merge

Inspired by ByDefault

Open in ChatGPTOpen in Claude
cell64px
solvetrue(replays the demo)
pushtrue
speed1.00×
seed7
<SlidePuzzle />

replays the demo when you let go.

Integrations
Plugs into the stack you already run.

Okto reads from and writes to forty-two tools your team already pays for, each one set up in a click.

Take this demo to your coding agent.
Add this moment from KeyframeUI to our project and adapt it to our content.

Slide Puzzle: Icon tiles in a faint grid of slots around your headline, moving like a sliding puzzle. The first time it scrolls into view the tiles start scrambled and the puzzle solves itself, one cell at a time; after that a tile slides into a free slot every few seconds. Sweep the mouse fast into a tile and it's shoved along the grid with the tiles in front of it; rest on one and its name rises above it.
This demo, Integrations: An ops assistant's integrations section: twelve tool tiles solve themselves into place round the headline, then drift. Each one is a link.

1. Install it:
   npx shadcn@latest add @keyframeui/slide-puzzle-demo
   (If the CLI can't find @keyframeui, use https://keyframeui.com/r/slide-puzzle-demo.json instead.)
   It adds the demo, components/slide-puzzle-demo.tsx, and the component, components/ui/slide-puzzle.tsx.

2. Make it ours: our copy, data, images and colors. Keep the structure the motion depends on. If we only need the effect, use <SlidePuzzle> on its own.

3. These props set its feel. Change them before you change the file:
   - cell (default 64): Size of one grid cell in px, tile and gap together. Tiles are three quarters of it. A narrow box shrinks its cells, to 70% at most, so six columns fit.
   - solve (default true): Start scrambled and solve the puzzle the first time it scrolls into view.
   - push (default true): Let a fast mouse sweep shove tiles along the grid, one cell, with the line of tiles in front.
   - speed (default 1): How fast every slide plays, as a multiple of the default pace: the solve's moves and their spacing, each drift, a push and a press. `2` is twice as fast.
   - seed (default 7): Picks the layout and the scramble. Change it for a different arrangement.

4. Don't change, in components/ui/slide-puzzle.tsx:
   - Tiles move only by `transform`, on WAAPI tweens written straight to their elements, never through React state. An interrupted tile retargets from where it is on screen, so it never jumps.
   - A tile only slides one cell along the grid into an empty slot, never into the hole, and drift and the scramble keep it within `band` cells of it. `occupant` changes with `at` on every move.
   - The solve is the scramble played backwards, each move waiting until the tile ahead has partly left the slot, so moves overlap but two tiles never share a slot.
   - Homes and the scramble come from the seeded PRNG, so a layout is the same on every visit, and `speed` is read through a ref as each move starts, so changing it never re-scrambles.
   - Reduced motion has no solve, drift, push or bump: tiles start home and a press moves at once, while labels and the first fade-in remain. The reduced flag is false while hydrating.
   Its timing is all in the TIMING block at the top of that file.

Docs: https://keyframeui.com/components/slide-puzzle.md

Installation

Add the component on its own with the CLI, or copy the file in by hand.

The CLI writes components/ui/slide-puzzle.tsx and installs clsx and tailwind-merge. It needs the @keyframeui registry in components.json; add it once per project.

npx shadcn@latest add @keyframeui/slide-puzzle

Examples

Finished sections built with Slide Puzzle. Each one installs whole, with the component included.

Team

A studio's careers section with the team as portrait tiles and three orange open seats. Rest on a face for a name and role.

Careers · 3 open seats

Come make small things feel right.

We're nine people in Lisbon and Oslo building the tools type designers open every morning. The orange tiles are waiting for someone.

Take this demo to your coding agent.
Add this moment from KeyframeUI to our project and adapt it to our content.

Slide Puzzle: Icon tiles in a faint grid of slots around your headline, moving like a sliding puzzle. The first time it scrolls into view the tiles start scrambled and the puzzle solves itself, one cell at a time; after that a tile slides into a free slot every few seconds. Sweep the mouse fast into a tile and it's shoved along the grid with the tiles in front of it; rest on one and its name rises above it.
This demo, Team: A studio's careers section with the team as portrait tiles and three orange open seats. Rest on a face for a name and role.

1. Install it:
   npx shadcn@latest add @keyframeui/slide-puzzle-team-demo
   (If the CLI can't find @keyframeui, use https://keyframeui.com/r/slide-puzzle-team-demo.json instead.)
   It adds the demo, components/slide-puzzle-team-demo.tsx, and the component, components/ui/slide-puzzle.tsx.

2. Make it ours: our copy, data, images and colors. Keep the structure the motion depends on. If we only need the effect, use <SlidePuzzle> on its own.

3. These props set its feel. Change them before you change the file:
   - cell (default 64): Size of one grid cell in px, tile and gap together. Tiles are three quarters of it. A narrow box shrinks its cells, to 70% at most, so six columns fit.
   - solve (default true): Start scrambled and solve the puzzle the first time it scrolls into view.
   - push (default true): Let a fast mouse sweep shove tiles along the grid, one cell, with the line of tiles in front.
   - speed (default 1): How fast every slide plays, as a multiple of the default pace: the solve's moves and their spacing, each drift, a push and a press. `2` is twice as fast.
   - seed (default 7): Picks the layout and the scramble. Change it for a different arrangement.

4. Don't change, in components/ui/slide-puzzle.tsx:
   - Tiles move only by `transform`, on WAAPI tweens written straight to their elements, never through React state. An interrupted tile retargets from where it is on screen, so it never jumps.
   - A tile only slides one cell along the grid into an empty slot, never into the hole, and drift and the scramble keep it within `band` cells of it. `occupant` changes with `at` on every move.
   - The solve is the scramble played backwards, each move waiting until the tile ahead has partly left the slot, so moves overlap but two tiles never share a slot.
   - Homes and the scramble come from the seeded PRNG, so a layout is the same on every visit, and `speed` is read through a ref as each move starts, so changing it never re-scrambles.
   - Reduced motion has no solve, drift, push or bump: tiles start home and a press moves at once, while labels and the first fade-in remain. The reduced flag is false while hydrating.
   Its timing is all in the TIMING block at the top of that file.

Docs: https://keyframeui.com/components/slide-puzzle.md

Not Found

A docs 404 where the numbered tiles are buttons: press one, or use the arrow keys, and it slides into a free slot.

Error 404

This page slid out of place.

The link points at an empty slot. Push a few tiles around while you're here, or head back to the docs.

Take this demo to your coding agent.
Add this moment from KeyframeUI to our project and adapt it to our content.

Slide Puzzle: Icon tiles in a faint grid of slots around your headline, moving like a sliding puzzle. The first time it scrolls into view the tiles start scrambled and the puzzle solves itself, one cell at a time; after that a tile slides into a free slot every few seconds. Sweep the mouse fast into a tile and it's shoved along the grid with the tiles in front of it; rest on one and its name rises above it.
This demo, Not Found: A docs 404 where the numbered tiles are buttons: press one, or use the arrow keys, and it slides into a free slot.

1. Install it:
   npx shadcn@latest add @keyframeui/slide-puzzle-404-demo
   (If the CLI can't find @keyframeui, use https://keyframeui.com/r/slide-puzzle-404-demo.json instead.)
   It adds the demo, components/slide-puzzle-404-demo.tsx, and the component, components/ui/slide-puzzle.tsx.

2. Make it ours: our copy, data, images and colors. Keep the structure the motion depends on. If we only need the effect, use <SlidePuzzle> on its own.

3. These props set its feel. Change them before you change the file:
   - cell (default 64): Size of one grid cell in px, tile and gap together. Tiles are three quarters of it. A narrow box shrinks its cells, to 70% at most, so six columns fit.
   - solve (default true): Start scrambled and solve the puzzle the first time it scrolls into view.
   - push (default true): Let a fast mouse sweep shove tiles along the grid, one cell, with the line of tiles in front.
   - speed (default 1): How fast every slide plays, as a multiple of the default pace: the solve's moves and their spacing, each drift, a push and a press. `2` is twice as fast.
   - seed (default 7): Picks the layout and the scramble. Change it for a different arrangement.

4. Don't change, in components/ui/slide-puzzle.tsx:
   - Tiles move only by `transform`, on WAAPI tweens written straight to their elements, never through React state. An interrupted tile retargets from where it is on screen, so it never jumps.
   - A tile only slides one cell along the grid into an empty slot, never into the hole, and drift and the scramble keep it within `band` cells of it. `occupant` changes with `at` on every move.
   - The solve is the scramble played backwards, each move waiting until the tile ahead has partly left the slot, so moves overlap but two tiles never share a slot.
   - Homes and the scramble come from the seeded PRNG, so a layout is the same on every visit, and `speed` is read through a ref as each move starts, so changing it never re-scrambles.
   - Reduced motion has no solve, drift, push or bump: tiles start home and a press moves at once, while labels and the first fade-in remain. The reduced flag is false while hydrating.
   Its timing is all in the TIMING block at the top of that file.

Docs: https://keyframeui.com/components/slide-puzzle.md

Props

tiles Required

TypeSlidePuzzleTile[]

The tiles, as { id, label, icon, href? }. label is the name that shows on hover and focus (and what screen readers announce), icon is an inline SVG or an <img>, centered and clipped to the tile. A tile with href is a link; without one it's a button that slides into a free slot when pressed.

children

TypeReact.ReactNode

Your headline and calls to action, centered. Their box, plus 10px, is the hole no tile enters.

cell

TypenumberDefault64

Size of one grid cell in px, tile and gap together. Tiles are three quarters of it. A narrow box shrinks its cells, to 70% at most, so six columns fit.

solve

TypebooleanDefaulttrue

Start scrambled and solve the puzzle the first time it scrolls into view.

drift

Typeboolean | numberDefaulttrue

Slide one tile into a free neighbouring slot every so often: true for every 2.4s, a number of ms, or false to keep still. It pauses under the pointer and off screen.

push

TypebooleanDefaulttrue

Let a fast mouse sweep shove tiles along the grid, one cell, with the line of tiles in front.

speed

TypenumberDefault1

How fast every slide plays, as a multiple of the default pace: the solve's moves and their spacing, each drift, a push and a press. 2 is twice as fast.

seed

TypenumberDefault7

Picks the layout and the scramble. Change it for a different arrangement.

className

Typestring

Extra classes for the root. The slots are drawn in its text color. Set the tile color with --slide-puzzle-tile (white, #18181b in dark), the focus ring with --slide-puzzle-ring (#ff5a1f) and the space above and below the headline with --slide-puzzle-inset (2.25 cells).