UI Components Medium
Sheet Navigation
Mobile bottom sheet with drag handle, slide-up animation, and action list. Swipe or tap to dismiss.
Open in Lab
MCP
css vanilla-js
Targets: JS HTML
Code
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root {
--bg: #050910;
--nav-bg: #0d1117;
--sheet-bg: #3b82f6;
--sheet-item: #1e3a6e;
--sheet-item-hover: #254a85;
--text: #e2e8f0;
--text-muted: #94a3b8;
--muted: #475569;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background: var(--bg);
color: var(--text);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 2rem;
}
/* โโ Demo wrapper โโ */
.demo {
width: 100%;
max-width: 400px;
display: flex;
flex-direction: column;
align-items: center;
gap: 2rem;
}
.demo-title {
font-size: 1.5rem;
font-weight: 800;
text-align: center;
}
.demo-sub {
color: var(--muted);
font-size: 0.875rem;
text-align: center;
}
/* โโ Phone frame โโ */
.phone-frame {
width: 320px;
border-radius: 32px;
border: 1.5px solid rgba(255, 255, 255, 0.08);
background: var(--nav-bg);
overflow: hidden;
box-shadow: 0 24px 64px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255, 255, 255, 0.04);
}
.phone-screen {
width: 100%;
height: 520px;
position: relative;
overflow: hidden;
display: flex;
flex-direction: column;
}
/* โโ Screen content โโ */
.screen-content {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 24px;
text-align: center;
}
.screen-content h2 {
font-size: 1.25rem;
font-weight: 700;
margin-bottom: 8px;
}
.screen-content p {
font-size: 0.85rem;
color: var(--text-muted);
line-height: 1.5;
}
/* โโ Open button โโ */
.open-btn {
position: absolute;
bottom: 28px;
left: 50%;
transform: translateX(-50%);
padding: 10px 28px;
border: none;
border-radius: 10px;
background: var(--sheet-bg);
color: #fff;
font-size: 0.85rem;
font-weight: 600;
cursor: pointer;
z-index: 5;
-webkit-tap-highlight-color: transparent;
}
.open-btn:active {
transform: translateX(-50%) scale(0.96);
opacity: 0.9;
}
/* โโ Backdrop โโ */
.backdrop {
position: absolute;
inset: 0;
background: rgba(0, 0, 0, 0.5);
opacity: 0;
pointer-events: none;
transition: opacity 0.3s ease;
z-index: 10;
}
.backdrop.visible {
opacity: 1;
pointer-events: all;
}
/* โโ Bottom sheet โโ */
.bottom-sheet {
position: absolute;
bottom: 0;
left: 0;
right: 0;
z-index: 11;
background: var(--sheet-bg);
border-radius: 20px 20px 0 0;
padding: 8px 16px 24px;
transform: translateY(100%);
transition: transform 0.35s cubic-bezier(0.32, 0.72, 0, 1);
max-height: 65%;
}
.bottom-sheet.open {
transform: translateY(0);
}
/* โโ Drag handle โโ */
.drag-handle {
width: 40px;
height: 4px;
background: rgba(255, 255, 255, 0.4);
border-radius: 2px;
margin: 8px auto 16px;
cursor: pointer;
}
/* โโ Sheet header โโ */
.sheet-header {
font-size: 1rem;
font-weight: 700;
color: #fff;
margin-bottom: 12px;
padding: 0 4px;
}
/* โโ Action rows โโ */
.sheet-actions {
display: flex;
flex-direction: column;
gap: 8px;
}
.action-row {
display: flex;
align-items: center;
gap: 14px;
width: 100%;
padding: 12px 14px;
border: none;
border-radius: 10px;
background: var(--sheet-item);
color: #fff;
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
transition: background 0.15s;
-webkit-tap-highlight-color: transparent;
}
.action-row:active {
background: var(--sheet-item-hover);
}
.action-row svg {
width: 20px;
height: 20px;
flex-shrink: 0;
}const openBtn = document.getElementById("openBtn");
const sheet = document.getElementById("sheet");
const backdrop = document.getElementById("backdrop");
const dragHandle = document.getElementById("dragHandle");
function openSheet() {
sheet.classList.add("open");
backdrop.classList.add("visible");
}
function closeSheet() {
sheet.classList.remove("open");
backdrop.classList.remove("visible");
}
openBtn.addEventListener("click", openSheet);
backdrop.addEventListener("click", closeSheet);
dragHandle.addEventListener("click", closeSheet);
document.querySelectorAll("[data-close]").forEach((el) => {
el.addEventListener("click", closeSheet);
});
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" />
<title>Sheet Navigation</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="demo">
<h1 class="demo-title">Sheet Navigation</h1>
<p class="demo-sub">Bottom sheet with drag handle and action list.</p>
<div class="phone-frame">
<div class="phone-screen">
<!-- Screen content -->
<div class="screen-content">
<h2>My App</h2>
<p>Tap the button below to open the sheet.</p>
</div>
<!-- Open button -->
<button class="open-btn" id="openBtn">Open Sheet</button>
<!-- Backdrop -->
<div class="backdrop" id="backdrop"></div>
<!-- Bottom sheet -->
<div class="bottom-sheet" id="sheet">
<div class="drag-handle" id="dragHandle"></div>
<div class="sheet-header">Actions</div>
<div class="sheet-actions">
<button class="action-row" data-close>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
<span>Home</span>
</button>
<button class="action-row" data-close>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<span>Search</span>
</button>
<button class="action-row" data-close>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
<span>Profile</span>
</button>
<button class="action-row" data-close>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
<span>Settings</span>
</button>
</div>
</div>
</div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>Sheet Navigation
A mobile bottom sheet that slides up from the bottom of the screen. Features a drag handle, action items, and dismissible backdrop.
Features
- Slide-up bottom sheet with smooth CSS transition
- Drag handle indicator at top
- Action list with icons
- Backdrop overlay that dismisses on tap
- Toggle button to open
How it works
- Button toggles
.sheet-openclass on the container - Sheet translates from
translateY(100%)to final position - Backdrop fades in behind the sheet
- Tap backdrop or drag handle to dismiss