Pure CSS Cards & galleries #hover

Flip card in pure CSS

Hover, tap or focus to flip. The back face is hidden until it turns toward you.

  • 36 lines of CSS
  • 6 lines of HTML
  • No JavaScript
  • No dependencies
  • MIT licensed
GitHub

How it works

  1. Two faces share one box. The back one starts pre-rotated by rotateY(180deg).
  2. backface-visibility: hidden hides whichever face is pointing away from you.
  3. On :hover / :focus rotate the inner wrapper, not the hovered element — otherwise the hit area rotates away and flickers.
  4. tabindex="0" makes it work on touch and keyboard too.

Key techniques

  • backface-visibility: hidden
  • transition
  • :hover / :focus-visible

Copy-paste code

HTML 6 lines

<div class="flip" tabindex="0">
  <div class="flip-inner">
    <div class="flip-face">Front</div>
    <div class="flip-face flip-back">Back</div>
  </div>
</div>

CSS 36 lines

.flip {
  width: 240px;
  height: 160px;
  perspective: 800px;
  cursor: pointer;
}

.flip-inner {
  position: relative;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
  transition: transform 0.8s cubic-bezier(0.3, 1.4, 0.5, 1);
}

.flip:hover .flip-inner,
.flip:focus .flip-inner {
  transform: rotateY(180deg);
}

.flip-face {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  border-radius: 16px;
  font: 700 1.5rem system-ui;
  color: #fff;
  background: linear-gradient(135deg, #8b6cff, #ff4d9d);
  backface-visibility: hidden;
}

.flip-back {
  background: linear-gradient(135deg, #2ee6d6, #8b6cff);
  transform: rotateY(180deg);
}

Sass source 47 lines

.d-flip {
  width: 190px;
  height: 125px;
  cursor: pointer;
  outline-offset: 6px;

  &__inner {
    position: relative;
    width: 100%;
    height: 100%;
    transform-style: preserve-3d;
    transition: transform 0.8s cubic-bezier(0.3, 1.4, 0.5, 1);
  }

  &:hover &__inner,
  &:focus &__inner {
    transform: rotateY(180deg);
  }

  &__face {
    position: absolute;
    inset: 0;
    display: grid;
    place-content: center;
    gap: 2px;
    border-radius: 16px;
    background: linear-gradient(135deg, var(--accent), var(--hot));
    color: #fff;
    text-align: center;
    backface-visibility: hidden;
    box-shadow: 0 16px 30px -14px var(--accent);

    b {
      font-size: 22px;
    }

    span {
      font-size: 12px;
      opacity: 0.85;
    }

    &--back {
      background: linear-gradient(135deg, var(--accent-2), var(--accent));
      transform: rotateY(180deg);
    }
  }
}
Standalone snippet · plain CSS, no build step, no dependencies.