UI Components Easy
Action Sheet
An iOS-style action sheet that slides up from the bottom with a list of actions and a separate cancel button. No libraries.
Open in Lab
MCP
vanilla-js css
Targets: JS HTML
Code
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
background: #f5f5f7;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.demo-page {
text-align: center;
padding: 40px 20px;
}
.demo-page h1 {
font-size: 24px;
font-weight: 700;
color: #111;
margin-bottom: 10px;
}
.demo-page p {
font-size: 15px;
color: #666;
margin-bottom: 32px;
}
.trigger-buttons {
display: flex;
gap: 12px;
justify-content: center;
flex-wrap: wrap;
}
.trigger-btn {
display: flex;
align-items: center;
gap: 8px;
padding: 13px 22px;
border: none;
border-radius: 12px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: transform 0.15s, opacity 0.15s;
background: #6366f1;
color: #fff;
}
.trigger-btn:active {
transform: scale(0.96);
opacity: 0.9;
}
.trigger-btn svg {
width: 16px;
height: 16px;
}
.trigger-btn.danger {
background: #ef4444;
}
/* Backdrop */
.sheet-backdrop {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.35);
opacity: 0;
pointer-events: none;
transition: opacity 0.25s ease;
z-index: 100;
}
.sheet-backdrop.visible {
opacity: 1;
pointer-events: all;
}
/* Action sheet */
.action-sheet {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 101;
padding: 0 8px 12px;
transform: translateY(100%);
transition: transform 0.35s cubic-bezier(0.32, 0.72, 0, 1);
max-width: 480px;
margin: 0 auto;
}
.action-sheet.open {
transform: translateY(0);
}
/* Sheet groups */
.sheet-group {
background: rgba(245, 245, 247, 0.95);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border-radius: 14px;
overflow: hidden;
margin-bottom: 8px;
}
.sheet-title {
padding: 14px 16px 6px;
font-size: 13px;
color: #888;
text-align: center;
font-weight: 500;
}
.sheet-subtitle {
padding: 0 16px 12px;
font-size: 12px;
color: #aaa;
text-align: center;
}
.sheet-action {
display: flex;
align-items: center;
gap: 14px;
width: 100%;
padding: 16px;
border: none;
background: none;
font-size: 16px;
color: #111;
cursor: pointer;
border-top: 1px solid rgba(0, 0, 0, 0.06);
transition: background 0.1s;
}
.sheet-action:first-of-type {
border-top: none;
}
.sheet-action:active {
background: rgba(0, 0, 0, 0.05);
}
.sheet-action svg {
width: 20px;
height: 20px;
flex-shrink: 0;
color: #6366f1;
}
.sheet-action.danger {
color: #ef4444;
font-weight: 600;
justify-content: center;
}
.sheet-action.danger svg {
color: #ef4444;
}
/* Cancel group */
.cancel-group .sheet-action.cancel {
justify-content: center;
font-weight: 600;
font-size: 17px;
color: #111;
}const backdrop = document.getElementById("backdrop");
let activeSheet = null;
function openSheet(id) {
const sheet = document.getElementById(`sheet-${id}`);
if (!sheet) return;
activeSheet = sheet;
backdrop.classList.add("visible");
sheet.classList.add("open");
document.body.style.overflow = "hidden";
}
function closeSheet() {
if (!activeSheet) return;
activeSheet.classList.remove("open");
backdrop.classList.remove("visible");
document.body.style.overflow = "";
activeSheet = null;
}
// Trigger buttons
document.querySelectorAll(".trigger-btn").forEach((btn) => {
btn.addEventListener("click", () => openSheet(btn.dataset.sheet));
});
// Backdrop
backdrop.addEventListener("click", closeSheet);
// Close buttons inside sheets
document.querySelectorAll("[data-close]").forEach((el) => {
el.addEventListener("click", closeSheet);
});
// Escape key
document.addEventListener("keydown", (e) => {
if (e.key === "Escape") closeSheet();
});<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="style.css" />
<title>Action Sheet</title>
</head>
<body>
<div class="demo-page">
<h1>Action Sheet</h1>
<p>Tap a button to open an action sheet</p>
<div class="trigger-buttons">
<button class="trigger-btn" data-sheet="share">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/></svg>
Share
</button>
<button class="trigger-btn danger" data-sheet="delete">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
Delete Item
</button>
</div>
</div>
<!-- Backdrop -->
<div class="sheet-backdrop" id="backdrop"></div>
<!-- Share Action Sheet -->
<div class="action-sheet" id="sheet-share" role="dialog" aria-modal="true" aria-label="Share options">
<div class="sheet-group">
<div class="sheet-title">Share Photo</div>
<button class="sheet-action" data-close>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"/><polyline points="16 6 12 2 8 6"/><line x1="12" y1="2" x2="12" y2="15"/></svg>
Share via Messages
</button>
<button class="sheet-action" data-close>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 8h1a4 4 0 0 1 0 8h-1"/><path d="M2 8h16v9a4 4 0 0 1-4 4H6a4 4 0 0 1-4-4V8z"/><line x1="6" y1="1" x2="6" y2="4"/><line x1="10" y1="1" x2="10" y2="4"/><line x1="14" y1="1" x2="14" y2="4"/></svg>
Copy Link
</button>
<button class="sheet-action" data-close>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
Save Image
</button>
<button class="sheet-action" data-close>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>
Embed
</button>
</div>
<div class="sheet-group cancel-group">
<button class="sheet-action cancel" data-close>Cancel</button>
</div>
</div>
<!-- Delete Action Sheet -->
<div class="action-sheet" id="sheet-delete" role="dialog" aria-modal="true" aria-label="Delete confirmation">
<div class="sheet-group">
<div class="sheet-title">Delete this item?</div>
<div class="sheet-subtitle">This action cannot be undone.</div>
<button class="sheet-action danger" data-close>Delete</button>
</div>
<div class="sheet-group cancel-group">
<button class="sheet-action cancel" data-close>Cancel</button>
</div>
</div>
<script src="script.js"></script>
</body>
</html>Action Sheet
An iOS-style action sheet that slides up from the bottom of the screen. Features a title, a scrollable list of contextual actions, and a separate Cancel button that is always visible.
How it works
- Trigger button opens the sheet by adding
.opento both the backdrop and the sheet - The sheet uses
translateY(100%)→translateY(0)for the slide-up animation - The Cancel button is rendered as a separate card below the actions, matching native iOS style
- Tapping the backdrop or Cancel closes the sheet
Variants in the demo
- Share — share, copy link, save image, embed
- Delete — destructive confirmation pattern with a red action
When to use it
- Contextual actions on a selected item
- Share sheets and save dialogs
- Confirmation for destructive actions (delete, leave, cancel subscription)