Long-press menu
For the three or four actions that belong to a single row, opened in place rather than on a screen of their own.
Live preview
Source
<div class="dc-lp">
<details class="dc-lp__box">
<summary class="dc-lp__item">
<span class="dc-lp__name">현장 점검 보고서.pdf</span>
<span class="dc-lp__meta">2.4MB · 어제 오후 5:12</span>
</summary>
<div class="dc-lp__menu" role="group" aria-label="파일 동작">
<button class="dc-lp__act" type="button">이름 바꾸기</button>
<button class="dc-lp__act" type="button">공유</button>
<button class="dc-lp__act dc-lp__act--danger" type="button">삭제</button>
</div>
</details>
<p class="dc-lp__hint">길게 누르면 열립니다. 짧게 눌러도 같은 메뉴가 열려 누구나 닿을 수 있습니다.</p>
</div>
.dc-lp {
padding: var(--dc-4);
background: var(--dc-bg);
font-family: var(--dc-font);
}
.dc-lp__box {
border: 1px solid var(--dc-line);
border-radius: var(--dc-r-md);
background: var(--dc-surface);
box-shadow: var(--dc-sh-1);
}
.dc-lp__item {
display: flex;
flex-direction: column;
gap: var(--dc-1);
min-height: 44px;
padding: var(--dc-3) var(--dc-4);
border-radius: var(--dc-r-md);
list-style: none;
cursor: pointer;
user-select: none;
-webkit-user-select: none;
-webkit-touch-callout: none;
}
.dc-lp__item::-webkit-details-marker { display: none; }
.dc-lp__item::marker { content: ""; }
.dc-lp__box[open] .dc-lp__item {
border-bottom: 1px solid var(--dc-line);
border-radius: var(--dc-r-md) var(--dc-r-md) 0 0;
background: var(--dc-sunken);
}
.dc-lp__name {
font-size: 15px;
font-weight: 600;
color: var(--dc-text);
}
.dc-lp__meta {
font-size: 12px;
color: var(--dc-text-mute);
}
.dc-lp__menu {
display: flex;
flex-direction: column;
padding: var(--dc-1);
}
.dc-lp__act {
display: flex;
align-items: center;
gap: var(--dc-3);
min-height: 48px;
padding: 0 var(--dc-3);
border: 0;
border-radius: var(--dc-r-sm);
background: none;
color: var(--dc-text);
font-family: var(--dc-font);
font-size: 15px;
text-align: left;
cursor: pointer;
transition: background var(--dc-fast);
}
.dc-lp__act:hover { background: var(--dc-sunken); }
.dc-lp__act--danger { color: var(--dc-danger); }
.dc-lp__ico {
width: 20px;
height: 20px;
flex: none;
fill: none;
stroke: currentColor;
stroke-width: 1.8;
stroke-linecap: round;
stroke-linejoin: round;
}
.dc-lp__hint {
margin: var(--dc-3) 0 0;
font-size: 12px;
line-height: 1.6;
color: var(--dc-text-mute);
}
document.querySelectorAll('.dc-lp__box').forEach(function (box) {
var item = box.querySelector('.dc-lp__item');
if (!item) return;
var timer = null;
function clear() {
if (timer) { clearTimeout(timer); timer = null; }
}
item.addEventListener('pointerdown', function () {
clear();
timer = setTimeout(function () {
timer = null;
box.open = true;
}, 450);
});
['pointerup', 'pointercancel', 'pointerleave'].forEach(function (name) {
item.addEventListener(name, clear);
});
});
How to use it
- Collect only actions scoped to that one item; screen-wide actions belong in the top bar
- Put the destructive action last and color it apart so a travelling finger does not hit it
- Keep the item itself visible above the menu so there is no doubt what the actions apply to
Watch out for
- A long press is invisible: make a short tap open it too, or pair it with a visible more button
- Built on details/summary the menu still opens when the script fails — the script only adds the long press
- Give menu rows about 48px of height; tight rows invite mis-taps