Pure CSS Scenes & objects #hover

Door in pure CSS

Hover or focus to open. The light behind is just the frame’s background.

  • 52 lines of CSS
  • 5 lines of HTML
  • No JavaScript
  • No dependencies
  • Snippet under MIT
GitHub

How it works

  1. The frame holds the perspective; the leaf rotates with transform-origin: left (the hinges).
  2. perspective-origin: 120% 50% moves the camera to the right of the frame, so the open door does not collapse into a thin line.
  3. The "light" is simply the frame’s background, revealed as the leaf swings away.
  4. tabindex="0" plus :focus makes it work by tap and by keyboard.
  5. Every length is a multiple of one base unit, --u, tied to the canvas: the frame is 150 × 250 units, so the door is the same share of a gallery card, the editor and a recording canvas. The shut door stands in the middle. The leaf swings out to the left, so as it opens the whole door slides right by half of that swing, on the same transition, and the open door plus its leaf is what sits in the middle then.

Key techniques

  • transform-origin: left
  • transition on :hover / :focus
  • perspective-origin

Copy-paste code

HTML 5 lines

<div class="scene">
  <div class="door" tabindex="0" role="img" aria-label="Door that swings open on hover or focus">
    <div class="leaf"><i></i></div>
  </div>
</div>

CSS 52 lines

.scene {
  /* one base unit: every length below is a multiple of it, so the door is the same share of a
     gallery card, the editor, a full screen and a recording canvas */
  --u: 0.21vmin;
  display: grid;
  place-items: center;
}

.door {
  box-sizing: content-box; /* the frame is drawn outside the leaf's opening: by design, on any page */
  position: relative;
  width: calc(150 * var(--u));
  height: calc(250 * var(--u));
  border: calc(8 * var(--u)) solid #3a2a1c;
  border-bottom: 0;
  background: radial-gradient(ellipse at 50% 70%, #fff6c9, #ffb547 55%, #7a4a12);
  perspective: calc(700 * var(--u));
  perspective-origin: 120% 50%;
  cursor: pointer;
  /* shut, the door is centred; open, the leaf reaches out to the left, so the door slides right
     by half of that to keep the door plus its leaf centred */
  transition: translate 0.9s cubic-bezier(0.3, 1.2, 0.5, 1);
}

.door:hover,
.door:focus {
  translate: calc(36 * var(--u)) 0;
}

.leaf {
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, #8a5a33, #6d4424);
  transform-origin: left center;
  transition: transform 0.9s cubic-bezier(0.3, 1.2, 0.5, 1);
}

.door:hover .leaf,
.door:focus .leaf {
  transform: rotateY(-105deg);
}

/* knob */
.leaf i {
  position: absolute;
  top: 50%;
  right: calc(12 * var(--u));
  width: calc(14 * var(--u));
  height: calc(14 * var(--u));
  border-radius: 50%;
  background: #ffd36b;
}
Standalone snippet · plain CSS, no build step, no dependencies.