Tile Flip Compare

(Tile by tile)

Turn over as much of the room as you want to see.

Each square flips between the fit-out we inherited and the one we handed back. Flip a few, or flip the lot.

0 of 12 squares flipped

Inherited

Handed back

About this block

Tile Flip ComparePRO

Two photographs split across the same grid so each square can be flipped from before to after on click, with buttons to turn the whole frame over and a counter of how much has been revealed.

Reveal12: Tile Flip Compare

The before and after are cut into a grid of squares, and each one flips on its own. Turn over a corner, a diagonal or the whole picture, with a counter and two controls under the frame.

Upgrade to Pro

Pro blocks install through the shadcn CLI with your license key and ship their full source. Docs and live previews stay open to everyone, so you can read every block's details first.

Upgrade Now

Installation

Swap YOUR_EMAIL and YOUR_KEY for the email and license key on your account. Find your license key on your account page.

Radix flavor

bash
npx shadcn add "https://ui.beste.co/r/reveal12?email=YOUR_EMAIL&license_key=YOUR_KEY"

Base UI flavor

bash
npx shadcn add "https://ui.beste.co/r-base/reveal12?email=YOUR_EMAIL&license_key=YOUR_KEY"

This installs the block to components/beste/block/reveal12.tsx plus the badge7 eyebrow it uses.

Quick start

The installed file exports reveal12Demo alongside the block: the exact props behind the preview above. Spread it to get a working comparison in one line.

tsx
import { Reveal12, reveal12Demo } from "@/components/beste/block/reveal12";

export default function Page() {
  return <Reveal12 {...reveal12Demo} />;
}

Then replace the demo with your own props. Written out, a trimmed setup looks like this:

tsx
import { Reveal12 } from "@/components/beste/block/reveal12";

export default function Page() {
  return (
    <Reveal12
      badge={{ label: "Tile by tile" }}
      heading="Turn over as much of the room as you want to see."
      description="Each square flips between the fit-out we inherited and the one we handed back."
      columns={4}
      rows={3}
      before={{ src: "/room/inherited.jpg", alt: "Room as inherited", label: "Inherited" }}
      after={{ src: "/room/handed-back.jpg", alt: "Room as handed back", label: "Handed back" }}
      labels={{
        revealAll: "Flip everything",
        coverAll: "Put it back",
        tileLabel: "Flip this square",
        counterSuffix: "squares flipped",
      }}
    />
  );
}

Props

PropTypeDefaultDescription
badge{ label: string }–Eyebrow label rendered as a Badge7
headingstring–Section heading in the left header column
descriptionstring–Supporting paragraph in the right header column
beforeCompareImage–The image every tile starts on
afterCompareImage–The image a flipped tile shows
columnsnumber4Tiles across the frame
rowsnumber3Tiles down the frame
labelsReveal12Labels{}Fixed strings that are not content: the two controls, the tile label and the counter suffix
classNamestring–Extra classes for the outer <section>
ts
type CompareImage = {
  src: string;
  alt: string;
  label: string;
};

type Reveal12Labels = {
  revealAll?: string;
  coverAll?: string;
  tileLabel?: string;
  counterSuffix?: string;
};

Behavior notes

  • Each tile shows one fragment of the whole photograph rather than a scaled copy: the image is sized to columns * 100% by rows * 100% and offset by the tile's own row and column, so the picture stays continuous across the grid.
  • The grid's aspectRatio is computed from columns / rows, so every tile is square whatever grid size you pass and the two source images are cropped identically.
  • Flipped tiles are tracked as an array of indexes, so the two controls are simple: reveal all sets the full list, cover all sets an empty one.
  • Every tile is a real button carrying aria-pressed and the shared tile label, and the fragment images use an empty alt because the picture is described once by the labelled states rather than twelve times.
  • The counter reads flipped of total with a caption suffix you supply, so it stays translatable without any string concatenation in the block.
  • The state labels sit under the frame at opposite ends of a md:grid-cols-2 row, which keeps the legend readable rather than pinning two pills onto a busy mosaic.
  • Grid size is fully driven by props: passing columns={6} rows={4} gives twenty-four tiles with no other change.

More Reveal blocks

View all Reveal
PRO

reveal8

Vertical Split Compare

PRO

reveal13

Hold To Compare

PRO

reveal7

Segmented State Compare

Get Pro access