emil kowalski ui building a hold to delete component
Nicholas
@nicholas
--- captured: 2026-05-23T12:03:16-07:00 tags: - ai-research - ui - design - curl-md --- --- description: A clever use of clip path. url:
- Uploaded
- Uploaded May 27, 2026
- File type
- MD
- Queried
- 0
Full document
Showing the full document.
---
source: https://emilkowal.ski/ui/building-a-hold-to-delete-component
author: Emil Kowalski
captured: 2026-05-23T12:03:16-07:00
tags:
- ai-research
- ui
- design
- curl-md
---
---
title: Building a Hold to Delete Component
description: A clever use of clip path.
url: https://emilkowal.ski/ui/building-a-hold-to-delete-component
site: Emil Kowalski
---
# Building a Hold to Delete Component
I recently shared [this component](https://x.com/emilkowalski/status/1901689149154869746) on X, and a lot of people liked it. This article briefly explains how it’s built.
Hold to Delete
Press and hold the button to see the transition.
## The starting point
Our starting point is a simple, styled button. You can use the code editor below to follow along.
App.jsstyles.css
```
"use client";
export default function ClipPathButton() {
return (
<button className="button">
<svg height="16" strokeLinejoin="round" viewBox="0 0 16 16" width="16">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M6.75 2.75C6.75 2.05964 7.30964 1.5 8 1.5C8.69036 1.5 9.25 2.05964 9.25 2.75V3H6.75V2.75ZM5.25 3V2.75C5.25 1.23122 6.48122 0 8 0C9.51878 0 10.75 1.23122 10.75 2.75V3H12.9201H14.25H15V4.5H14.25H13.8846L13.1776 13.6917C13.0774 14.9942 11.9913 16 10.6849 16H5.31508C4.00874 16 2.92263 14.9942 2.82244 13.6917L2.11538 4.5H1.75H1V3H1.75H3.07988H5.25ZM4.31802 13.5767L3.61982 4.5H12.3802L11.682 13.5767C11.6419 14.0977 11.2075 14.5 10.6849 14.5H5.31508C4.79254 14.5 4.3581 14.0977 4.31802 13.5767Z"
fill="currentColor"
/>
</svg>
Hold to Delete
</button>
);
}
```
## Setting up the structure
Before we take care of the reveal transition, we need two versions of the button: the default monochrome one, and the version we want to gradually reveal.
```
<button className="button">
<div aria-hidden="true" className="hold-overlay">
<TrashIcon />
Hold to Delete
</div>
<TrashIcon />
Hold to Delete
</button>
```
In this case, `.button` has `position: relative` and `.hold-overlay` is absolutely positioned on top of it. This creates the following effect:
Hold to Delete
Hold to Delete
## Revealing the overlay
Before revealing the overlay, we need to hide it first. We’ll use the `inset` shape of the `clip-path` property and set the right value to 100%. You can think of each `inset` value as a side of a rectangle, **similar to the margin or padding** shorthand.
```
.hold-overlay {
clip-path: inset(0px 100% 0px 0px);
}
```
This hides the red overlay from the right. Why from the right? Because by subtracting from our 100%, we can reveal the button from the left, which will create the effect we want. I cover `clip-path` in more detail in [The Magic of Clip Path](/ui/the-magic-of-clip-path).
To reveal it, we’ll use the `:active` pseudo-class. While the user holds the button, we’ll set `clip-path` to `inset(0px 0px 0px 0px)`.
```
.button:active .hold-overlay {
clip-path: inset(0px 0px 0px 0px);
}
```
This will make the overlay visible instantly once the button is pressed:
Hold to Delete
Hold to Delete
To add an animation, we’ll use the `transition` property. Since we want a longer effect, we’ll use a duration of `2s`. We should use `linear` timing function to reveal the overlay evenly.
```
.hold-overlay {
transition: clip-path 2s linear;
}
```
Hold to Delete
Hold to Delete
That’s it! Let’s now add some polish to our button to make it feel better.
## Polishing it up
The first thing that feels off is the transition when you release the button. Pressing should be slow to allow the user to confirm their choice, but the release can be much snappier.
To do that, I’ll set the `.hold-overlay` transition to `200ms` with an `ease-out` timing function. Then, in the `:active` state I’ll override it with a `2s` transition using a `linear` timing function.
```
.hold-overlay {
transition: clip-path 200ms ease-out;
}
.button:active .hold-overlay {
transition: clip-path 2s linear;
}
```
This makes the release transition much faster while keeping the press transition slow enough for the user to confirm their choice. Remember: [you should make your animations fast](/ui/great-animations#great-animations-are-fast) (most of the time).
One thing that makes *any* button feel better is a slight scale-down animation on press. It feels good because it gives the user instant feedback, making the UI feel more responsive, as if it’s really listening to the user.
To do that, I’ll add `transform: scale(0.97)` to the button’s `:active` state and apply a snappy transition.
```
.button {
transition: transform 160ms ease-out;
}
.button:active {
transform: scale(0.97);
}
```
This gives us a nice hold to delete button. Here’s the final code:
App.jsstyles.css
```
"use client";
export default function ClipPathButton() {
return (
<button className="button">
<div aria-hidden="true" className="hold-overlay">
<svg height="16" strokeLinejoin="round" viewBox="0 0 16 16" width="16">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M6.75 2.75C6.75 2.05964 7.30964 1.5 8 1.5C8.69036 1.5 9.25 2.05964 9.25 2.75V3H6.75V2.75ZM5.25 3V2.75C5.25 1.23122 6.48122 0 8 0C9.51878 0 10.75 1.23122 10.75 2.75V3H12.9201H14.25H15V4.5H14.25H13.8846L13.1776 13.6917C13.0774 14.9942 11.9913 16 10.6849 16H5.31508C4.00874 16 2.92263 14.9942 2.82244 13.6917L2.11538 4.5H1.75H1V3H1.75H3.07988H5.25ZM4.31802 13.5767L3.61982 4.5H12.3802L11.682 13.5767C11.6419 14.0977 11.2075 14.5 10.6849 14.5H5.31508C4.79254 14.5 4.3581 14.0977 4.31802 13.5767Z"
fill="currentColor"
/>
</svg>
Hold to Delete
</div>
<svg height="16" strokeLinejoin="round" viewBox="0 0 16 16" width="16">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M6.75 2.75C6.75 2.05964 7.30964 1.5 8 1.5C8.69036 1.5 9.25 2.05964 9.25 2.75V3H6.75V2.75ZM5.25 3V2.75C5.25 1.23122 6.48122 0 8 0C9.51878 0 10.75 1.23122 10.75 2.75V3H12.9201H14.25H15V4.5H14.25H13.8846L13.1776 13.6917C13.0774 14.9942 11.9913 16 10.6849 16H5.31508C4.00874 16 2.92263 14.9942 2.82244 13.6917L2.11538 4.5H1.75H1V3H1.75H3.07988H5.25ZM4.31802 13.5767L3.61982 4.5H12.3802L11.682 13.5767C11.6419 14.0977 11.2075 14.5 10.6849 14.5H5.31508C4.79254 14.5 4.3581 14.0977 4.31802 13.5767Z"
fill="currentColor"
/>
</svg>
Hold to Delete
</button>
);
}
```
## More components like this
This component is coming from my animations course in which we build a lot of other things ranging from simple CSS animations to complex Framer Motion (now Motion) components.
[Check out animations.dev](https://animations.dev/)
Some of the components we build in the course.
[NextBuilding a Drawer Component](/ui/building-a-drawer-component)
---
Powered by [curl.md](https://curl.md)
Want to learn more?
Ask about this document