28 CSS Close Buttons17 / 28

Pure CSSMIT licensed

Trash Compactor Close Button

An industrial squeeze: the × sits between two heavy press plates that slam vertically inward on hover, crushing the glyph — bars compress via scaleY, bow outward via scaleX, and the whole assembly settles into a flattened bar with a satisfying clunk bezier. Great for delete-flavored dismissals.

Published Updated

Live Demo
Try it

The code

<section class="ccb-17" aria-label="Trash compactor close button demo">
  <button class="ccb-17__close" type="button" aria-label="Close">
    <span class="ccb-17__plate ccb-17__plate--top" aria-hidden="true"></span>
    <span class="ccb-17__x" aria-hidden="true"></span>
    <span class="ccb-17__plate ccb-17__plate--bot" aria-hidden="true"></span>
  </button>
</section>
.ccb-17,
.ccb-17 *,
.ccb-17 *::before,
.ccb-17 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.ccb-17 {
  --crush: .22;
  --steel: oklch(0.75 0.02 250);
  --ease: cubic-bezier(.6,-.28,.55,1.32);
  font-family: 'Segoe UI',system-ui,sans-serif;
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 48px 24px;
  background: linear-gradient(180deg,oklch(0.32 0.02 255),oklch(0.22 0.015 250));
}

.ccb-17 .ccb-17__close {
  position: relative;
  display: grid;
  place-items: center;
  width: 76px;
  height: 76px;
  border: 1px solid oklch(0.5 0.02 250 / .6);
  border-radius: 14px;
  background: linear-gradient(180deg,oklch(0.4 0.02 250),oklch(0.3 0.02 250));
  cursor: pointer;
  overflow: hidden;
  box-shadow: 0 16px 36px -18px rgba(0,0,0,.85),inset 0 1px 0 oklch(0.55 0.02 250 / .5);
}

.ccb-17 .ccb-17__plate {
  position: absolute;
  inset-inline: 10px;
  height: 8px;
  border-radius: 3px;
  background: linear-gradient(180deg,var(--steel),oklch(0.55 0.02 250));
  box-shadow: 0 2px 4px rgba(0,0,0,.5);
  transition: transform .34s var(--ease);
}

.ccb-17 .ccb-17__plate--top {
  top: 8px;
}

.ccb-17 .ccb-17__plate--bot {
  bottom: 8px;
}

.ccb-17 .ccb-17__x {
  position: relative;
  width: 24px;
  height: 24px;
  transform-origin: center;
  transition: transform .34s var(--ease);
}

.ccb-17 .ccb-17__x::before,
.ccb-17 .ccb-17__x::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 24px;
  height: 3px;
  border-radius: 2px;
  background: oklch(0.92 0.01 250);
  transition: border-radius .34s var(--ease);
}

.ccb-17 .ccb-17__x::before {
  transform: translate(-50%,-50%) rotate(45deg);
}

.ccb-17 .ccb-17__x::after {
  transform: translate(-50%,-50%) rotate(-45deg);
}

.ccb-17 .ccb-17__close:hover .ccb-17__plate--top {
  transform: translateY(15px);
}

.ccb-17 .ccb-17__close:hover .ccb-17__plate--bot {
  transform: translateY(-15px);
}

.ccb-17 .ccb-17__close:hover .ccb-17__x {
  transform: scaleY(var(--crush)) scaleX(1.25);
}

.ccb-17 .ccb-17__close:hover .ccb-17__x::before,
.ccb-17 .ccb-17__close:hover .ccb-17__x::after {
  border-radius: 4px;
}

.ccb-17 .ccb-17__close:active {
  transform: scale(.95);
}

.ccb-17 .ccb-17__close:focus-visible {
  outline: 3px solid oklch(0.8 0.1 250);
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  .ccb-17 .ccb-17__plate,
    .ccb-17 .ccb-17__x {
    transition: none;
  }
}
/* No JavaScript — plates translate inward while the × scaleY-crushes and scaleX-bows; one anticipation bezier unifies the machine. */
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Close Button from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Trash Compactor Close Button
Source: https://codefronts.com/components/css-close-buttons/trash-compactor/

An industrial squeeze: the × sits between two heavy press plates that slam vertically inward on hover, crushing the glyph — bars compress via scaleY, bow outward via scaleX, and the whole assembly settles into a flattened bar with a satisfying clunk bezier. Great for delete-flavored dismissals.
## HTML
```html
<section class="ccb-17" aria-label="Trash compactor close button demo">
  <button class="ccb-17__close" type="button" aria-label="Close">
    <span class="ccb-17__plate ccb-17__plate--top" aria-hidden="true"></span>
    <span class="ccb-17__x" aria-hidden="true"></span>
    <span class="ccb-17__plate ccb-17__plate--bot" aria-hidden="true"></span>
  </button>
</section>
```
## CSS
```css
.ccb-17,
.ccb-17 *,
.ccb-17 *::before,
.ccb-17 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.ccb-17 {
  --crush: .22;
  --steel: oklch(0.75 0.02 250);
  --ease: cubic-bezier(.6,-.28,.55,1.32);
  font-family: 'Segoe UI',system-ui,sans-serif;
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 48px 24px;
  background: linear-gradient(180deg,oklch(0.32 0.02 255),oklch(0.22 0.015 250));
}

.ccb-17 .ccb-17__close {
  position: relative;
  display: grid;
  place-items: center;
  width: 76px;
  height: 76px;
  border: 1px solid oklch(0.5 0.02 250 / .6);
  border-radius: 14px;
  background: linear-gradient(180deg,oklch(0.4 0.02 250),oklch(0.3 0.02 250));
  cursor: pointer;
  overflow: hidden;
  box-shadow: 0 16px 36px -18px rgba(0,0,0,.85),inset 0 1px 0 oklch(0.55 0.02 250 / .5);
}

.ccb-17 .ccb-17__plate {
  position: absolute;
  inset-inline: 10px;
  height: 8px;
  border-radius: 3px;
  background: linear-gradient(180deg,var(--steel),oklch(0.55 0.02 250));
  box-shadow: 0 2px 4px rgba(0,0,0,.5);
  transition: transform .34s var(--ease);
}

.ccb-17 .ccb-17__plate--top {
  top: 8px;
}

.ccb-17 .ccb-17__plate--bot {
  bottom: 8px;
}

.ccb-17 .ccb-17__x {
  position: relative;
  width: 24px;
  height: 24px;
  transform-origin: center;
  transition: transform .34s var(--ease);
}

.ccb-17 .ccb-17__x::before,
.ccb-17 .ccb-17__x::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 24px;
  height: 3px;
  border-radius: 2px;
  background: oklch(0.92 0.01 250);
  transition: border-radius .34s var(--ease);
}

.ccb-17 .ccb-17__x::before {
  transform: translate(-50%,-50%) rotate(45deg);
}

.ccb-17 .ccb-17__x::after {
  transform: translate(-50%,-50%) rotate(-45deg);
}

.ccb-17 .ccb-17__close:hover .ccb-17__plate--top {
  transform: translateY(15px);
}

.ccb-17 .ccb-17__close:hover .ccb-17__plate--bot {
  transform: translateY(-15px);
}

.ccb-17 .ccb-17__close:hover .ccb-17__x {
  transform: scaleY(var(--crush)) scaleX(1.25);
}

.ccb-17 .ccb-17__close:hover .ccb-17__x::before,
.ccb-17 .ccb-17__close:hover .ccb-17__x::after {
  border-radius: 4px;
}

.ccb-17 .ccb-17__close:active {
  transform: scale(.95);
}

.ccb-17 .ccb-17__close:focus-visible {
  outline: 3px solid oklch(0.8 0.1 250);
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  .ccb-17 .ccb-17__plate,
    .ccb-17 .ccb-17__x {
    transition: none;
  }
}
```

## JavaScript
```js
/* No JavaScript — plates translate inward while the × scaleY-crushes and scaleX-bows; one anticipation bezier unifies the machine. */
```

How this works

Three synchronized transforms sell the crush. The plates (two absolutely-positioned bars at the button's top and bottom edges) translate toward centre. The × wrapper compresses with scaleY(.22) — and simultaneously bows with scaleX(1.25), because real crushed material displaces sideways; that conservation-of-volume detail is what makes it physical rather than just "shrinking".

Every piece shares one cubic-bezier(.6,-.28,.55,1.32) — a slight anticipation dip then overshoot — so plates and glyph move as one machine. The bars' border-radius grows during the crush (squashed things get blunt edges). Release runs the same transition in reverse automatically.

Make it yours

  • Crush depth via --crush (scaleY target, .22 default).
  • Plate thickness/color via --plate vars for lighter or heavier machinery.
  • Trigger on :active for crush-on-press (fits destructive delete confirmation).
  • Slow to .6s and the effect turns comedic — good for playful empty-state dismissals.

Gotchas — read before shipping

  • Scale the wrapper, not each bar independently — independent scaling desyncs the crossing point of the ×.
  • The sideways bow (scaleX) must be subtle (≤1.3) or the glyph reads as melting, not crushing.
  • Set transform-origin:center explicitly on the wrapper; inherited origins from earlier layout tweaks are the classic cause of lopsided crushes.

Browser support

ChromeSafariFirefoxEdge
111+16.4+113+111+

Pure transform choreography — runs on anything; oklch steel tones are the only modern syntax.

Techniques used in this demo

Search CodeFronts

Loading…