51 CSS Buttons12 / 51
CSS Confirm Delete Button
A destructive action that asks once, inline, without a modal: the delete button expands to reveal "Cancel / Yes, delete" with the confirm option deliberately placed second and never under the original cursor position. Grid-track animation, a checkbox for state, and a lid-flip trash icon — all pure CSS.
Published Updated
The code
<div class="cb-12">
<div class="cb-12__stage">
<span class="cb-12__shell">
<label class="cb-12__trigger">
<input type="checkbox" aria-label="Delete this project, requires confirmation">
<svg viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
<g class="cb-12__lid"><path d="M4.5 7h15"/><path d="M9.5 7V5.2A1.2 1.2 0 0 1 10.7 4h2.6a1.2 1.2 0 0 1 1.2 1.2V7"/></g>
<path d="M6.5 7.8 7.4 19a1.5 1.5 0 0 0 1.5 1.4h6.2a1.5 1.5 0 0 0 1.5-1.4l.9-11.2"/><path d="M10.5 11v6M13.5 11v6"/>
</svg>
<span class="cb-12__triggerLabel">Delete project</span>
</label>
<span class="cb-12__confirm">
<span class="cb-12__confirmInner">
<span class="cb-12__ask">Sure?</span>
<button class="cb-12__yes" type="button">Yes, delete</button>
</span>
</span>
</span>
</div>
</div><div class="cb-12">
<div class="cb-12__stage">
<span class="cb-12__shell">
<label class="cb-12__trigger">
<input type="checkbox" aria-label="Delete this project, requires confirmation">
<svg viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
<g class="cb-12__lid"><path d="M4.5 7h15"/><path d="M9.5 7V5.2A1.2 1.2 0 0 1 10.7 4h2.6a1.2 1.2 0 0 1 1.2 1.2V7"/></g>
<path d="M6.5 7.8 7.4 19a1.5 1.5 0 0 0 1.5 1.4h6.2a1.5 1.5 0 0 0 1.5-1.4l.9-11.2"/><path d="M10.5 11v6M13.5 11v6"/>
</svg>
<span class="cb-12__triggerLabel">Delete project</span>
</label>
<span class="cb-12__confirm">
<span class="cb-12__confirmInner">
<span class="cb-12__ask">Sure?</span>
<button class="cb-12__yes" type="button">Yes, delete</button>
</span>
</span>
</span>
</div>
</div>@import url('https://fonts.googleapis.com/css2?family=Schibsted+Grotesk:wght@400;500;600;700&display=swap');
.cb-12 {
--bg: #f6f5f3;
--surface: #ffffff;
--line: #e3e1dc;
--text: #1a1817;
--muted: #6d6862;
--danger: #b91c1c;
--danger-ink: #ffffff;
--radius: .75rem;
--sans: "Schibsted Grotesk",ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif;
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: block;
background: var(--bg);
color: var(--text);
font-family: var(--sans);
-webkit-font-smoothing: antialiased;
}
@supports (color: oklch(50% 0 0)) {
.cb-12 {
--bg: oklch(96.8% .004 90);
--danger: oklch(48% .2 27);
--line: oklch(90% .008 90);
}
}
@media (prefers-color-scheme: dark) {
.cb-12:not([data-theme="light"]) {
--bg: #0d0c0c;
--surface: #171514;
--line: #2b2827;
--text: #f4f1ee;
--muted: #a29b95;
--danger: #ef4444;
--danger-ink: #180505;
}
}
.cb-12[data-theme="dark"] {
--bg: #0d0c0c;
--surface: #171514;
--line: #2b2827;
--text: #f4f1ee;
--muted: #a29b95;
--danger: #ef4444;
--danger-ink: #180505;
}
.cb-12,
.cb-12 *,
.cb-12 *::before,
.cb-12 *::after {
box-sizing: border-box;
}
.cb-12__stage {
min-height: 100vh;
min-height: 100svh;
display: grid;
place-items: center;
padding: clamp(1.5rem,6vw,4rem);
}
.cb-12__shell {
display: grid;
grid-template-columns: auto 0fr;
align-items: center;
padding: .35rem;
border-radius: calc(var(--radius) + .35rem);
background: var(--surface);
border: 1px solid var(--line);
box-shadow: 0 14px 34px -22px color-mix(in oklab,var(--text) 65%,transparent);
transition: grid-template-columns .45s cubic-bezier(.16,1,.3,1), border-color .3s ease;
}
.cb-12__trigger {
cursor: pointer;
display: inline-flex;
align-items: center;
gap: .55rem;
min-height: 2.75rem;
padding: 0 1rem;
border-radius: var(--radius);
font-size: .9375rem;
font-weight: 600;
color: var(--muted);
transition: color .22s ease, background-color .22s ease;
-webkit-tap-highlight-color: transparent;
}
.cb-12__trigger input {
position: absolute;
width: 1px;
height: 1px;
opacity: 0;
clip-path: inset(50%);
}
.cb-12__trigger svg {
width: 1.2rem;
height: 1.2rem;
flex: none;
}
.cb-12__lid {
transform-origin: 18% 60%;
transition: rotate .38s cubic-bezier(.16,1,.3,1), translate .38s cubic-bezier(.16,1,.3,1);
}
.cb-12__trigger:hover {
color: var(--danger);
background: color-mix(in oklab,var(--danger) 8%,transparent);
}
.cb-12__shell:has(input:focus-visible) {
outline: 2px solid var(--danger);
outline-offset: 3px;
}
.cb-12__shell:has(input:checked) {
grid-template-columns: auto 1fr;
border-color: color-mix(in oklab,var(--danger) 45%,var(--line));
}
.cb-12__shell:has(input:checked) .cb-12__trigger {
color: var(--danger);
}
.cb-12__shell:has(input:checked) .cb-12__lid {
rotate: -24deg;
translate: -1px -1.5px;
}
.cb-12__shell:has(input:checked) .cb-12__triggerLabel {
text-decoration: line-through;
text-decoration-color: color-mix(in oklab,var(--danger) 60%,transparent);
}
.cb-12__confirm {
min-width: 0;
overflow: hidden;
}
.cb-12__confirmInner {
display: flex;
align-items: center;
gap: .6rem;
padding-left: .5rem;
padding-right: .15rem;
opacity: 0;
translate: -.5rem 0;
transition: opacity .3s ease .08s, translate .4s cubic-bezier(.16,1,.3,1) .08s;
}
.cb-12__shell:has(input:checked) .cb-12__confirmInner {
opacity: 1;
translate: 0 0;
}
.cb-12__ask {
font-size: .8125rem;
font-weight: 600;
color: var(--muted);
white-space: nowrap;
}
.cb-12__yes {
cursor: pointer;
white-space: nowrap;
border: 0;
border-radius: var(--radius);
min-height: 2.75rem;
padding: 0 1.1rem;
font: inherit;
font-size: .875rem;
font-weight: 700;
color: var(--danger-ink);
background: var(--danger);
box-shadow: 0 8px 18px -12px color-mix(in oklab,var(--danger) 85%,transparent);
transition: background-color .2s ease, translate .2s cubic-bezier(.16,1,.3,1);
}
.cb-12__yes:hover {
background: color-mix(in oklab,var(--danger) 85%,black);
translate: 0 -1px;
}
.cb-12__yes:active {
translate: 0 1px;
}
.cb-12__yes:focus-visible {
outline: 2px solid var(--danger);
outline-offset: 3px;
}
@media (prefers-reduced-motion: reduce) {
.cb-12__shell,
.cb-12__confirmInner,
.cb-12__lid,
.cb-12__yes,
.cb-12__trigger {
transition: none;
}
}
@media (forced-colors: active) {
.cb-12__shell {
border: 1px solid ButtonText;
}
.cb-12__yes {
background: Highlight;
color: HighlightText;
}
}@import url('https://fonts.googleapis.com/css2?family=Schibsted+Grotesk:wght@400;500;600;700&display=swap');
.cb-12 {
--bg: #f6f5f3;
--surface: #ffffff;
--line: #e3e1dc;
--text: #1a1817;
--muted: #6d6862;
--danger: #b91c1c;
--danger-ink: #ffffff;
--radius: .75rem;
--sans: "Schibsted Grotesk",ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif;
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: block;
background: var(--bg);
color: var(--text);
font-family: var(--sans);
-webkit-font-smoothing: antialiased;
}
@supports (color: oklch(50% 0 0)) {
.cb-12 {
--bg: oklch(96.8% .004 90);
--danger: oklch(48% .2 27);
--line: oklch(90% .008 90);
}
}
@media (prefers-color-scheme: dark) {
.cb-12:not([data-theme="light"]) {
--bg: #0d0c0c;
--surface: #171514;
--line: #2b2827;
--text: #f4f1ee;
--muted: #a29b95;
--danger: #ef4444;
--danger-ink: #180505;
}
}
.cb-12[data-theme="dark"] {
--bg: #0d0c0c;
--surface: #171514;
--line: #2b2827;
--text: #f4f1ee;
--muted: #a29b95;
--danger: #ef4444;
--danger-ink: #180505;
}
.cb-12,
.cb-12 *,
.cb-12 *::before,
.cb-12 *::after {
box-sizing: border-box;
}
.cb-12__stage {
min-height: 100vh;
min-height: 100svh;
display: grid;
place-items: center;
padding: clamp(1.5rem,6vw,4rem);
}
.cb-12__shell {
display: grid;
grid-template-columns: auto 0fr;
align-items: center;
padding: .35rem;
border-radius: calc(var(--radius) + .35rem);
background: var(--surface);
border: 1px solid var(--line);
box-shadow: 0 14px 34px -22px color-mix(in oklab,var(--text) 65%,transparent);
transition: grid-template-columns .45s cubic-bezier(.16,1,.3,1), border-color .3s ease;
}
.cb-12__trigger {
cursor: pointer;
display: inline-flex;
align-items: center;
gap: .55rem;
min-height: 2.75rem;
padding: 0 1rem;
border-radius: var(--radius);
font-size: .9375rem;
font-weight: 600;
color: var(--muted);
transition: color .22s ease, background-color .22s ease;
-webkit-tap-highlight-color: transparent;
}
.cb-12__trigger input {
position: absolute;
width: 1px;
height: 1px;
opacity: 0;
clip-path: inset(50%);
}
.cb-12__trigger svg {
width: 1.2rem;
height: 1.2rem;
flex: none;
}
.cb-12__lid {
transform-origin: 18% 60%;
transition: rotate .38s cubic-bezier(.16,1,.3,1), translate .38s cubic-bezier(.16,1,.3,1);
}
.cb-12__trigger:hover {
color: var(--danger);
background: color-mix(in oklab,var(--danger) 8%,transparent);
}
.cb-12__shell:has(input:focus-visible) {
outline: 2px solid var(--danger);
outline-offset: 3px;
}
.cb-12__shell:has(input:checked) {
grid-template-columns: auto 1fr;
border-color: color-mix(in oklab,var(--danger) 45%,var(--line));
}
.cb-12__shell:has(input:checked) .cb-12__trigger {
color: var(--danger);
}
.cb-12__shell:has(input:checked) .cb-12__lid {
rotate: -24deg;
translate: -1px -1.5px;
}
.cb-12__shell:has(input:checked) .cb-12__triggerLabel {
text-decoration: line-through;
text-decoration-color: color-mix(in oklab,var(--danger) 60%,transparent);
}
.cb-12__confirm {
min-width: 0;
overflow: hidden;
}
.cb-12__confirmInner {
display: flex;
align-items: center;
gap: .6rem;
padding-left: .5rem;
padding-right: .15rem;
opacity: 0;
translate: -.5rem 0;
transition: opacity .3s ease .08s, translate .4s cubic-bezier(.16,1,.3,1) .08s;
}
.cb-12__shell:has(input:checked) .cb-12__confirmInner {
opacity: 1;
translate: 0 0;
}
.cb-12__ask {
font-size: .8125rem;
font-weight: 600;
color: var(--muted);
white-space: nowrap;
}
.cb-12__yes {
cursor: pointer;
white-space: nowrap;
border: 0;
border-radius: var(--radius);
min-height: 2.75rem;
padding: 0 1.1rem;
font: inherit;
font-size: .875rem;
font-weight: 700;
color: var(--danger-ink);
background: var(--danger);
box-shadow: 0 8px 18px -12px color-mix(in oklab,var(--danger) 85%,transparent);
transition: background-color .2s ease, translate .2s cubic-bezier(.16,1,.3,1);
}
.cb-12__yes:hover {
background: color-mix(in oklab,var(--danger) 85%,black);
translate: 0 -1px;
}
.cb-12__yes:active {
translate: 0 1px;
}
.cb-12__yes:focus-visible {
outline: 2px solid var(--danger);
outline-offset: 3px;
}
@media (prefers-reduced-motion: reduce) {
.cb-12__shell,
.cb-12__confirmInner,
.cb-12__lid,
.cb-12__yes,
.cb-12__trigger {
transition: none;
}
}
@media (forced-colors: active) {
.cb-12__shell {
border: 1px solid ButtonText;
}
.cb-12__yes {
background: Highlight;
color: HighlightText;
}
}Here's a working CSS 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: CSS Confirm Delete Button
Source: https://codefronts.com/components/css-buttons/css-confirm-delete-button/
A destructive action that asks once, inline, without a modal: the delete button expands to reveal "Cancel / Yes, delete" with the confirm option deliberately placed second and never under the original cursor position. Grid-track animation, a checkbox for state, and a lid-flip trash icon — all pure CSS.
## HTML
```html
<div class="cb-12">
<div class="cb-12__stage">
<span class="cb-12__shell">
<label class="cb-12__trigger">
<input type="checkbox" aria-label="Delete this project, requires confirmation">
<svg viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
<g class="cb-12__lid"><path d="M4.5 7h15"/><path d="M9.5 7V5.2A1.2 1.2 0 0 1 10.7 4h2.6a1.2 1.2 0 0 1 1.2 1.2V7"/></g>
<path d="M6.5 7.8 7.4 19a1.5 1.5 0 0 0 1.5 1.4h6.2a1.5 1.5 0 0 0 1.5-1.4l.9-11.2"/><path d="M10.5 11v6M13.5 11v6"/>
</svg>
<span class="cb-12__triggerLabel">Delete project</span>
</label>
<span class="cb-12__confirm">
<span class="cb-12__confirmInner">
<span class="cb-12__ask">Sure?</span>
<button class="cb-12__yes" type="button">Yes, delete</button>
</span>
</span>
</span>
</div>
</div>
```
## CSS
```css
@import url('https://fonts.googleapis.com/css2?family=Schibsted+Grotesk:wght@400;500;600;700&display=swap');
.cb-12 {
--bg: #f6f5f3;
--surface: #ffffff;
--line: #e3e1dc;
--text: #1a1817;
--muted: #6d6862;
--danger: #b91c1c;
--danger-ink: #ffffff;
--radius: .75rem;
--sans: "Schibsted Grotesk",ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif;
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: block;
background: var(--bg);
color: var(--text);
font-family: var(--sans);
-webkit-font-smoothing: antialiased;
}
@supports (color: oklch(50% 0 0)) {
.cb-12 {
--bg: oklch(96.8% .004 90);
--danger: oklch(48% .2 27);
--line: oklch(90% .008 90);
}
}
@media (prefers-color-scheme: dark) {
.cb-12:not([data-theme="light"]) {
--bg: #0d0c0c;
--surface: #171514;
--line: #2b2827;
--text: #f4f1ee;
--muted: #a29b95;
--danger: #ef4444;
--danger-ink: #180505;
}
}
.cb-12[data-theme="dark"] {
--bg: #0d0c0c;
--surface: #171514;
--line: #2b2827;
--text: #f4f1ee;
--muted: #a29b95;
--danger: #ef4444;
--danger-ink: #180505;
}
.cb-12,
.cb-12 *,
.cb-12 *::before,
.cb-12 *::after {
box-sizing: border-box;
}
.cb-12__stage {
min-height: 100vh;
min-height: 100svh;
display: grid;
place-items: center;
padding: clamp(1.5rem,6vw,4rem);
}
.cb-12__shell {
display: grid;
grid-template-columns: auto 0fr;
align-items: center;
padding: .35rem;
border-radius: calc(var(--radius) + .35rem);
background: var(--surface);
border: 1px solid var(--line);
box-shadow: 0 14px 34px -22px color-mix(in oklab,var(--text) 65%,transparent);
transition: grid-template-columns .45s cubic-bezier(.16,1,.3,1), border-color .3s ease;
}
.cb-12__trigger {
cursor: pointer;
display: inline-flex;
align-items: center;
gap: .55rem;
min-height: 2.75rem;
padding: 0 1rem;
border-radius: var(--radius);
font-size: .9375rem;
font-weight: 600;
color: var(--muted);
transition: color .22s ease, background-color .22s ease;
-webkit-tap-highlight-color: transparent;
}
.cb-12__trigger input {
position: absolute;
width: 1px;
height: 1px;
opacity: 0;
clip-path: inset(50%);
}
.cb-12__trigger svg {
width: 1.2rem;
height: 1.2rem;
flex: none;
}
.cb-12__lid {
transform-origin: 18% 60%;
transition: rotate .38s cubic-bezier(.16,1,.3,1), translate .38s cubic-bezier(.16,1,.3,1);
}
.cb-12__trigger:hover {
color: var(--danger);
background: color-mix(in oklab,var(--danger) 8%,transparent);
}
.cb-12__shell:has(input:focus-visible) {
outline: 2px solid var(--danger);
outline-offset: 3px;
}
.cb-12__shell:has(input:checked) {
grid-template-columns: auto 1fr;
border-color: color-mix(in oklab,var(--danger) 45%,var(--line));
}
.cb-12__shell:has(input:checked) .cb-12__trigger {
color: var(--danger);
}
.cb-12__shell:has(input:checked) .cb-12__lid {
rotate: -24deg;
translate: -1px -1.5px;
}
.cb-12__shell:has(input:checked) .cb-12__triggerLabel {
text-decoration: line-through;
text-decoration-color: color-mix(in oklab,var(--danger) 60%,transparent);
}
.cb-12__confirm {
min-width: 0;
overflow: hidden;
}
.cb-12__confirmInner {
display: flex;
align-items: center;
gap: .6rem;
padding-left: .5rem;
padding-right: .15rem;
opacity: 0;
translate: -.5rem 0;
transition: opacity .3s ease .08s, translate .4s cubic-bezier(.16,1,.3,1) .08s;
}
.cb-12__shell:has(input:checked) .cb-12__confirmInner {
opacity: 1;
translate: 0 0;
}
.cb-12__ask {
font-size: .8125rem;
font-weight: 600;
color: var(--muted);
white-space: nowrap;
}
.cb-12__yes {
cursor: pointer;
white-space: nowrap;
border: 0;
border-radius: var(--radius);
min-height: 2.75rem;
padding: 0 1.1rem;
font: inherit;
font-size: .875rem;
font-weight: 700;
color: var(--danger-ink);
background: var(--danger);
box-shadow: 0 8px 18px -12px color-mix(in oklab,var(--danger) 85%,transparent);
transition: background-color .2s ease, translate .2s cubic-bezier(.16,1,.3,1);
}
.cb-12__yes:hover {
background: color-mix(in oklab,var(--danger) 85%,black);
translate: 0 -1px;
}
.cb-12__yes:active {
translate: 0 1px;
}
.cb-12__yes:focus-visible {
outline: 2px solid var(--danger);
outline-offset: 3px;
}
@media (prefers-reduced-motion: reduce) {
.cb-12__shell,
.cb-12__confirmInner,
.cb-12__lid,
.cb-12__yes,
.cb-12__trigger {
transition: none;
}
}
@media (forced-colors: active) {
.cb-12__shell {
border: 1px solid ButtonText;
}
.cb-12__yes {
background: Highlight;
color: HighlightText;
}
}
```Here's a working CSS 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: CSS Confirm Delete Button
Source: https://codefronts.com/components/css-buttons/css-confirm-delete-button/
A destructive action that asks once, inline, without a modal: the delete button expands to reveal "Cancel / Yes, delete" with the confirm option deliberately placed second and never under the original cursor position. Grid-track animation, a checkbox for state, and a lid-flip trash icon — all pure CSS.
## HTML
```html
<div class="cb-12">
<div class="cb-12__stage">
<span class="cb-12__shell">
<label class="cb-12__trigger">
<input type="checkbox" aria-label="Delete this project, requires confirmation">
<svg viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
<g class="cb-12__lid"><path d="M4.5 7h15"/><path d="M9.5 7V5.2A1.2 1.2 0 0 1 10.7 4h2.6a1.2 1.2 0 0 1 1.2 1.2V7"/></g>
<path d="M6.5 7.8 7.4 19a1.5 1.5 0 0 0 1.5 1.4h6.2a1.5 1.5 0 0 0 1.5-1.4l.9-11.2"/><path d="M10.5 11v6M13.5 11v6"/>
</svg>
<span class="cb-12__triggerLabel">Delete project</span>
</label>
<span class="cb-12__confirm">
<span class="cb-12__confirmInner">
<span class="cb-12__ask">Sure?</span>
<button class="cb-12__yes" type="button">Yes, delete</button>
</span>
</span>
</span>
</div>
</div>
```
## CSS
```css
@import url('https://fonts.googleapis.com/css2?family=Schibsted+Grotesk:wght@400;500;600;700&display=swap');
.cb-12 {
--bg: #f6f5f3;
--surface: #ffffff;
--line: #e3e1dc;
--text: #1a1817;
--muted: #6d6862;
--danger: #b91c1c;
--danger-ink: #ffffff;
--radius: .75rem;
--sans: "Schibsted Grotesk",ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif;
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: block;
background: var(--bg);
color: var(--text);
font-family: var(--sans);
-webkit-font-smoothing: antialiased;
}
@supports (color: oklch(50% 0 0)) {
.cb-12 {
--bg: oklch(96.8% .004 90);
--danger: oklch(48% .2 27);
--line: oklch(90% .008 90);
}
}
@media (prefers-color-scheme: dark) {
.cb-12:not([data-theme="light"]) {
--bg: #0d0c0c;
--surface: #171514;
--line: #2b2827;
--text: #f4f1ee;
--muted: #a29b95;
--danger: #ef4444;
--danger-ink: #180505;
}
}
.cb-12[data-theme="dark"] {
--bg: #0d0c0c;
--surface: #171514;
--line: #2b2827;
--text: #f4f1ee;
--muted: #a29b95;
--danger: #ef4444;
--danger-ink: #180505;
}
.cb-12,
.cb-12 *,
.cb-12 *::before,
.cb-12 *::after {
box-sizing: border-box;
}
.cb-12__stage {
min-height: 100vh;
min-height: 100svh;
display: grid;
place-items: center;
padding: clamp(1.5rem,6vw,4rem);
}
.cb-12__shell {
display: grid;
grid-template-columns: auto 0fr;
align-items: center;
padding: .35rem;
border-radius: calc(var(--radius) + .35rem);
background: var(--surface);
border: 1px solid var(--line);
box-shadow: 0 14px 34px -22px color-mix(in oklab,var(--text) 65%,transparent);
transition: grid-template-columns .45s cubic-bezier(.16,1,.3,1), border-color .3s ease;
}
.cb-12__trigger {
cursor: pointer;
display: inline-flex;
align-items: center;
gap: .55rem;
min-height: 2.75rem;
padding: 0 1rem;
border-radius: var(--radius);
font-size: .9375rem;
font-weight: 600;
color: var(--muted);
transition: color .22s ease, background-color .22s ease;
-webkit-tap-highlight-color: transparent;
}
.cb-12__trigger input {
position: absolute;
width: 1px;
height: 1px;
opacity: 0;
clip-path: inset(50%);
}
.cb-12__trigger svg {
width: 1.2rem;
height: 1.2rem;
flex: none;
}
.cb-12__lid {
transform-origin: 18% 60%;
transition: rotate .38s cubic-bezier(.16,1,.3,1), translate .38s cubic-bezier(.16,1,.3,1);
}
.cb-12__trigger:hover {
color: var(--danger);
background: color-mix(in oklab,var(--danger) 8%,transparent);
}
.cb-12__shell:has(input:focus-visible) {
outline: 2px solid var(--danger);
outline-offset: 3px;
}
.cb-12__shell:has(input:checked) {
grid-template-columns: auto 1fr;
border-color: color-mix(in oklab,var(--danger) 45%,var(--line));
}
.cb-12__shell:has(input:checked) .cb-12__trigger {
color: var(--danger);
}
.cb-12__shell:has(input:checked) .cb-12__lid {
rotate: -24deg;
translate: -1px -1.5px;
}
.cb-12__shell:has(input:checked) .cb-12__triggerLabel {
text-decoration: line-through;
text-decoration-color: color-mix(in oklab,var(--danger) 60%,transparent);
}
.cb-12__confirm {
min-width: 0;
overflow: hidden;
}
.cb-12__confirmInner {
display: flex;
align-items: center;
gap: .6rem;
padding-left: .5rem;
padding-right: .15rem;
opacity: 0;
translate: -.5rem 0;
transition: opacity .3s ease .08s, translate .4s cubic-bezier(.16,1,.3,1) .08s;
}
.cb-12__shell:has(input:checked) .cb-12__confirmInner {
opacity: 1;
translate: 0 0;
}
.cb-12__ask {
font-size: .8125rem;
font-weight: 600;
color: var(--muted);
white-space: nowrap;
}
.cb-12__yes {
cursor: pointer;
white-space: nowrap;
border: 0;
border-radius: var(--radius);
min-height: 2.75rem;
padding: 0 1.1rem;
font: inherit;
font-size: .875rem;
font-weight: 700;
color: var(--danger-ink);
background: var(--danger);
box-shadow: 0 8px 18px -12px color-mix(in oklab,var(--danger) 85%,transparent);
transition: background-color .2s ease, translate .2s cubic-bezier(.16,1,.3,1);
}
.cb-12__yes:hover {
background: color-mix(in oklab,var(--danger) 85%,black);
translate: 0 -1px;
}
.cb-12__yes:active {
translate: 0 1px;
}
.cb-12__yes:focus-visible {
outline: 2px solid var(--danger);
outline-offset: 3px;
}
@media (prefers-reduced-motion: reduce) {
.cb-12__shell,
.cb-12__confirmInner,
.cb-12__lid,
.cb-12__yes,
.cb-12__trigger {
transition: none;
}
}
@media (forced-colors: active) {
.cb-12__shell {
border: 1px solid ButtonText;
}
.cb-12__yes {
background: Highlight;
color: HighlightText;
}
}
```How this works
The expansion is a grid whose second track animates from 0fr to 1fr — the modern way to animate to intrinsic content width without max-width hacks:
.cb-12__shell{ display:grid; grid-template-columns:auto 0fr;
transition:grid-template-columns .45s cubic-bezier(.16,1,.3,1); }
.cb-12__shell:has(input:checked){ grid-template-columns:auto 1fr; }
.cb-12__confirm{ min-width:0; overflow:hidden; }The trash lid rotates off using transform-origin at its hinge, so one path becomes an animation:
.cb-12__lid{ transform-origin:18% 60%; transition:rotate .35s, translate .35s; }
.cb-12__shell:has(input:checked) .cb-12__lid{ rotate:-22deg; translate:-1px -1px; }Safety comes from layout, not colour: the confirm button appears to the right of the cancel, so a second reflexive click lands on "Cancel". The trigger is a checkbox label (Space toggles, focus visible), and the confirm control is a real <button> so your handler binds where you expect.
Make it yours
- Reverse the option order for platforms where confirm-first is the convention — swap the two children, nothing else.
- Add a countdown by putting a
@keyframeswidth animation on the confirm's::after; auto-collapse needs JS though. - Change
--dangeronce to match your palette; the ghost cancel derives from the neutral tokens. - For a heavier action (delete account) add a typed-confirmation input into the revealed slot — the grid track grows to fit.
- Set
grid-template-columns:auto 1fron load if you want the confirmation always visible in a bulk-edit toolbar.
Gotchas — read before shipping
0frtrack animation needs the child to havemin-width:0andoverflow:hidden, otherwise the content refuses to compress.- Never make the confirm button appear exactly where the trigger was — double-clickers will delete their data. Offset it.
- Colour alone must not carry "this is destructive": the word "delete" is in the label for a reason.
- For genuinely irreversible actions, an inline confirm is not enough; use a dialog with focus trapping and a typed confirmation.
- A checkbox is fine for a self-contained widget, but if this lives in a form give it
form=""so it isn't serialised as data.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.2+ | 121+ | 111+ |
Floor set by :has(), oklch(), color-mix() as this demo is written. The core technique itself goes back further — Chrome 107+.
Animating grid-template-columns needs Chrome 107 / Safari 16 / Firefox 66; :has() needs Chrome 105 / Safari 15.4 / Firefox 121. Without :has() the state toggle needs a sibling selector or a class from JS — the layout technique is unchanged.