Web InnoventixFreeCode

Book Open Effect

Original · free

Image opens like a book cover to reveal detail on hover.

byWeb InnoventixReact + Tailwind
bookopenimageeffect
Open

Copy prompt gives Claude Code, Cursor or v0 a ready-to-paste prompt that recreates this exact component.

npx shadcn@latest add https://webinnoventix.com/r/imgfx-book-open.json
imgfx-book-open.tsx
"use client";

export default function ImgfxBookOpen() {
  return (
    <section className="relative w-full overflow-hidden bg-neutral-50 px-6 py-24 text-neutral-900 dark:bg-neutral-950 dark:text-neutral-50 sm:px-10 lg:py-32">
      <style>{`
        @keyframes bookopen_hinthinge {
          0%, 100% { transform: rotateY(0deg); }
          50% { transform: rotateY(-26deg); }
        }
        .bookopen-scene { perspective: 2200px; }
        .bookopen-cover {
          transform-origin: left center;
          transform: rotateY(0deg);
          transition: transform 900ms cubic-bezier(0.22, 1, 0.36, 1),
                      box-shadow 900ms cubic-bezier(0.22, 1, 0.36, 1);
          backface-visibility: hidden;
          will-change: transform;
        }
        .bookopen-book:hover .bookopen-cover,
        .bookopen-book:focus-within .bookopen-cover {
          transform: rotateY(-118deg);
          box-shadow: 40px 30px 60px -20px rgba(0,0,0,0.55);
        }
        .bookopen-spine-glow {
          opacity: 0;
          transition: opacity 900ms ease;
        }
        .bookopen-book:hover .bookopen-spine-glow,
        .bookopen-book:focus-within .bookopen-spine-glow {
          opacity: 1;
        }
        .bookopen-inside {
          transform: scale(1.06);
          transition: transform 1100ms cubic-bezier(0.22, 1, 0.36, 1);
        }
        .bookopen-book:hover .bookopen-inside,
        .bookopen-book:focus-within .bookopen-inside {
          transform: scale(1);
        }
        .bookopen-nudge {
          animation: bookopen_hinthinge 5.5s ease-in-out 1.2s 2 both;
          transform-origin: left center;
        }
        @media (prefers-reduced-motion: reduce) {
          .bookopen-cover,
          .bookopen-inside,
          .bookopen-spine-glow { transition: none; }
          .bookopen-book:hover .bookopen-cover,
          .bookopen-book:focus-within .bookopen-cover { transform: rotateY(-118deg); }
          .bookopen-nudge { animation: none; }
        }
      `}</style>

      <div className="mx-auto flex max-w-4xl flex-col items-center text-center">
        <p className="mb-3 text-[0.7rem] font-semibold uppercase tracking-[0.4em] text-indigo-600 dark:text-indigo-400">
          Image Effect
        </p>
        <h2 className="text-3xl font-semibold tracking-tight sm:text-4xl">
          Book Open
        </h2>
        <p className="mt-3 max-w-md text-sm text-neutral-600 dark:text-neutral-400">
          The cover swings back on its hinge to reveal the story printed inside.
          <span className="ml-1 font-medium text-neutral-900 dark:text-neutral-100">
            Hover to open.
          </span>
        </p>

        <div className="mt-16 flex w-full justify-center">
          <div className="bookopen-scene">
            <div
              className="bookopen-book group relative h-[380px] w-[300px] cursor-pointer outline-none sm:h-[460px] sm:w-[360px]"
              tabIndex={0}
              role="button"
              aria-label="Open the book cover to reveal the inside photograph"
            >
              {/* Page-block shadow under the book */}
              <div
                className="absolute inset-0 translate-x-2 translate-y-2 rounded-r-md rounded-l-sm bg-neutral-300 shadow-xl dark:bg-neutral-800"
                aria-hidden="true"
              />

              {/* Inside spread (revealed) */}
              <div className="absolute inset-0 overflow-hidden rounded-r-md rounded-l-sm bg-neutral-200 shadow-inner dark:bg-neutral-900">
                <div className="bookopen-inside h-full w-full">
                  {/* eslint-disable-next-line @next/next/no-img-element */}
                  <img
                    src="/img/gallery/g14.webp"
                    alt="Detailed photograph revealed on the inside page of the book"
                    loading="lazy"
                    draggable={false}
                    className="h-full w-full object-cover"
                  />
                </div>
                {/* inner-margin gutter shadow near the hinge */}
                <div
                  className="pointer-events-none absolute inset-y-0 left-0 w-14 bg-gradient-to-r from-black/35 to-transparent"
                  aria-hidden="true"
                />
                <div className="pointer-events-none absolute bottom-3 right-4 text-right">
                  <span className="rounded-full bg-black/55 px-3 py-1 text-[0.65rem] font-medium uppercase tracking-widest text-white backdrop-blur-sm">
                    Inside
                  </span>
                </div>
              </div>

              {/* The swinging cover */}
              <div className="bookopen-nudge absolute inset-0" style={{ transformStyle: "preserve-3d" }}>
                <div className="bookopen-cover absolute inset-0 overflow-hidden rounded-r-md rounded-l-sm shadow-2xl">
                  {/* eslint-disable-next-line @next/next/no-img-element */}
                  <img
                    src="/img/gallery/g05.webp"
                    alt="Front cover of the book showing the featured photograph"
                    loading="lazy"
                    draggable={false}
                    className="h-full w-full object-cover"
                  />
                  {/* cover tint + vignette for depth */}
                  <div
                    className="pointer-events-none absolute inset-0 bg-gradient-to-br from-black/10 via-transparent to-black/45"
                    aria-hidden="true"
                  />
                  {/* spine highlight along the hinge */}
                  <div
                    className="pointer-events-none absolute inset-y-0 left-0 w-6 bg-gradient-to-r from-white/25 via-white/5 to-transparent"
                    aria-hidden="true"
                  />
                  {/* cover title plate */}
                  <div className="pointer-events-none absolute inset-x-0 bottom-0 p-5">
                    <p className="text-[0.6rem] font-semibold uppercase tracking-[0.35em] text-white/70">
                      Volume 05
                    </p>
                    <p className="mt-1 text-lg font-semibold tracking-tight text-white drop-shadow">
                      Field Notes in Light
                    </p>
                  </div>
                </div>

                {/* warm glow that leaks from the spine as it opens */}
                <div
                  className="bookopen-spine-glow pointer-events-none absolute inset-y-4 left-0 w-8 rounded-full bg-amber-200/60 blur-xl dark:bg-amber-300/40"
                  aria-hidden="true"
                />
              </div>
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}

Dependencies

None (React + Tailwind only).

Licence

Built by Web Innoventix. Free for personal and commercial use, no attribution required.

Built by Web Innoventix

Need a custom interface, a full website, or SEO that gets you cited by AI? We design, build and rank it end to end.

Get a free quote

Similar components

Browse all →