Menu ngăn kéo
Dành cho nhóm mục quá nhiều để nhét vào thanh tab nhưng lại ít dùng đến mức không xứng một ô tab.
Xem trước trực tiếp
Nguồn
<div class="dc-dw">
<input class="dc-dw__sw" id="dc-dw-open" type="checkbox" aria-expanded="true" aria-controls="dc-dw-panel" checked>
<label class="dc-dw__trigger" for="dc-dw-open">
<span aria-hidden="true">☰</span><span class="dc-sr">전체 메뉴 열고 닫기</span>
</label>
<label class="dc-dw__scrim" for="dc-dw-open"><span class="dc-sr">메뉴 닫기</span></label>
<nav class="dc-dw__panel" id="dc-dw-panel" aria-label="전체 메뉴">
<div class="dc-dw__me">
<span class="dc-dw__avatar" aria-hidden="true">김</span>
<span class="dc-dw__who">
<b class="dc-dw__name">김하늘</b>
<span class="dc-dw__sub">쿠폰 2장 · 포인트 1,200</span>
</span>
</div>
<ul class="dc-dw__list">
<li>
<a class="dc-dw__item" href="#" aria-current="page">
<span class="dc-dw__ico" aria-hidden="true">⌂</span>홈
</a>
</li>
<li>
<a class="dc-dw__item" href="#">
<span class="dc-dw__ico" aria-hidden="true">▤</span>주문 내역
<span class="dc-dw__count" aria-hidden="true">3</span>
<span class="dc-sr">배송 중 3건</span>
</a>
</li>
<li>
<a class="dc-dw__item" href="#">
<span class="dc-dw__ico" aria-hidden="true">♡</span>찜한 상품
</a>
</li>
</ul>
<hr class="dc-dw__sep">
<ul class="dc-dw__list">
<li>
<a class="dc-dw__item" href="#">
<span class="dc-dw__ico" aria-hidden="true">⚙</span>설정
</a>
</li>
<li>
<a class="dc-dw__item" href="#">
<span class="dc-dw__ico" aria-hidden="true">☏</span>고객센터
</a>
</li>
</ul>
</nav>
</div>
.dc-dw {
position: relative; overflow: hidden;
min-height: 420px;
background: var(--dc-bg);
font-family: var(--dc-font);
}
.dc-dw__sw {
position: absolute; top: 0; left: 0;
width: 1px; height: 1px; opacity: 0;
margin: 0; pointer-events: none;
}
.dc-dw__trigger {
display: grid; place-items: center;
width: 44px; height: 44px;
margin: var(--dc-2); margin-top: calc(var(--dc-safe-top) + var(--dc-2));
border-radius: var(--dc-r-full);
color: var(--dc-text); font-size: 20px; line-height: 1;
cursor: pointer;
}
.dc-dw__sw:focus-visible + .dc-dw__trigger {
outline: 2px solid var(--dc-brand); outline-offset: 2px;
}
.dc-dw__scrim {
position: absolute; inset: 0;
background: var(--dc-overlay);
opacity: 0; visibility: hidden; cursor: pointer;
transition: opacity var(--dc-slow), visibility var(--dc-slow);
}
.dc-dw__panel {
position: absolute; top: 0; bottom: 0; left: 0;
width: 294px; max-width: 84%;
display: flex; flex-direction: column; gap: 2px;
padding: calc(var(--dc-safe-top) + var(--dc-4)) var(--dc-3) calc(var(--dc-safe-bottom) + var(--dc-4));
background: var(--dc-surface);
border-right: 1px solid var(--dc-line);
box-shadow: var(--dc-sh-3);
overflow-y: auto;
transform: translateX(-103%);
transition: transform var(--dc-slow);
}
.dc-dw__sw:checked ~ .dc-dw__scrim { opacity: 1; visibility: visible; }
.dc-dw__sw:checked ~ .dc-dw__panel { transform: translateX(0); }
.dc-dw__me {
display: flex; align-items: center; gap: var(--dc-3);
padding: var(--dc-2) var(--dc-2) var(--dc-4);
}
.dc-dw__avatar {
flex: 0 0 auto;
display: grid; place-items: center;
width: 44px; height: 44px; border-radius: var(--dc-r-full);
background: var(--dc-brand-soft); color: var(--dc-brand-text);
font-size: 17px; font-weight: 800;
}
.dc-dw__who { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.dc-dw__list { display: flex; flex-direction: column; gap: 2px; }
.dc-dw__name { font-size: 16px; font-weight: 700; color: var(--dc-text); }
.dc-dw__sub { font-size: 12px; color: var(--dc-text-sub); }
.dc-dw__item {
display: flex; align-items: center; gap: var(--dc-3);
min-height: 48px; padding: 0 var(--dc-3);
border-radius: var(--dc-r-md);
color: var(--dc-text-sub); text-decoration: none;
font-size: 15px; font-weight: 600;
transition: background var(--dc-fast), color var(--dc-fast);
}
.dc-dw__item:active { background: var(--dc-sunken); }
.dc-dw__item[aria-current="page"] {
background: var(--dc-brand-soft); color: var(--dc-brand-text);
}
.dc-dw__ico {
flex: 0 0 auto; width: 22px; text-align: center;
font-size: 17px; line-height: 1;
}
.dc-dw__count {
margin-left: auto;
min-width: 20px; height: 20px; padding: 0 6px;
border-radius: var(--dc-r-full);
background: var(--dc-danger); color: var(--dc-text-on);
font-size: 11px; font-weight: 700; line-height: 20px; text-align: center;
}
.dc-dw__sep {
height: 1px; margin: var(--dc-3) var(--dc-3);
border: 0; background: var(--dc-line);
}
.dc-dw--right .dc-dw__trigger { margin-left: auto; }
.dc-dw--right .dc-dw__panel {
left: auto; right: 0;
border-right: 0; border-left: 1px solid var(--dc-line);
transform: translateX(103%);
}
.dc-dw--right .dc-dw__sw:checked ~ .dc-dw__panel { transform: translateX(0); }
Cách dùng
- Mục hay dùng thì không để ở đây — đưa lên thanh tab; ngăn kéo luôn xa hơn một lần chạm
- Đặt thông tin tài khoản ở trên cùng; nửa lý do người ta mở ngăn kéo là để xem nó
- Chạm vùng tối bên ngoài cũng phải đóng được — chỉ một đường ra làm người dùng thấy bị mắc
- Hơn mười mục thì chia bằng đường kẻ; hơn hai mươi thì nên là một màn hình riêng
Điểm cần lưu ý
- Dùng checkbox để mở/đóng thì không cần script, nhưng phải chạm được bằng bàn phím — giữ tên dc-sr và viền focus
- Panel cần --dc-safe-top và --dc-safe-bottom, nếu không sẽ chạy vào tai thỏ và thanh home
- Khi ngăn kéo mở, nội dung phía sau không nên đọc được; hãy đặt aria-hidden cho phần nền