Sheet dưới
Dùng khi cần chọn hoặc điền đúng một thứ mà không rời khỏi màn hình, trượt lên từ đáy và che một phần trang.
Xem trước trực tiếp
Nguồn
<div class="dc-bs-stage">
<div class="dc-bs-behind" aria-hidden="true">
<div class="dc-bs-behind__bar"></div>
<div class="dc-bs-behind__line"></div>
<div class="dc-bs-behind__line"></div>
</div>
<div class="dc-bs-scrim" aria-hidden="true"></div>
<section class="dc-bs" role="dialog" aria-modal="true" aria-labelledby="dc-bs-t">
<span class="dc-bs__grab" aria-hidden="true"></span>
<div class="dc-bs__head">
<h2 class="dc-bs__title" id="dc-bs-t">배송 방법 고르기</h2>
<button class="dc-bs__close" type="button">
<span class="dc-sr">닫기</span>
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M6.5 6.5l11 11M17.5 6.5l-11 11" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
</button>
</div>
<div class="dc-bs__body">
<label class="dc-bs__opt">
<input class="dc-bs__radio" type="radio" name="dc-bs-ship" checked>
<span class="dc-bs__opt-main">
<span class="dc-bs__opt-name">일반 배송</span>
<span class="dc-bs__opt-desc">9월 14일(월) 도착 예정</span>
</span>
<span class="dc-bs__opt-price">무료</span>
</label>
<label class="dc-bs__opt">
<input class="dc-bs__radio" type="radio" name="dc-bs-ship">
<span class="dc-bs__opt-main">
<span class="dc-bs__opt-name">새벽 배송</span>
<span class="dc-bs__opt-desc">내일 오전 7시 전 도착</span>
</span>
<span class="dc-bs__opt-price">3,000원</span>
</label>
<label class="dc-bs__opt">
<input class="dc-bs__radio" type="radio" name="dc-bs-ship">
<span class="dc-bs__opt-main">
<span class="dc-bs__opt-name">매장에서 받기</span>
<span class="dc-bs__opt-desc">성수점 · 오늘 오후 6시부터</span>
</span>
<span class="dc-bs__opt-price">무료</span>
</label>
</div>
<div class="dc-bs__foot">
<button class="dc-bs__cta" type="button">이 방법으로 받기</button>
</div>
</section>
</div>
.dc-bs-stage {
position: relative; overflow: hidden;
height: 520px;
background: var(--dc-bg);
border-radius: var(--dc-r-md);
font-family: var(--dc-font);
}
.dc-bs-behind { padding: calc(var(--dc-safe-top) + var(--dc-6)) var(--dc-5) 0; }
.dc-bs-behind__bar { width: 56%; height: 20px; border-radius: var(--dc-r-full); background: var(--dc-sunken); }
.dc-bs-behind__line { height: 11px; margin-top: var(--dc-3); border-radius: var(--dc-r-full); background: var(--dc-sunken); }
.dc-bs-behind__line:last-child { width: 64%; }
.dc-bs-scrim { position: absolute; inset: 0; background: var(--dc-overlay); }
.dc-bs {
position: absolute; left: 0; right: 0; bottom: 0;
display: flex; flex-direction: column;
max-height: 88%;
background: var(--dc-surface);
border-top: 1px solid var(--dc-line);
border-radius: var(--dc-r-lg) var(--dc-r-lg) 0 0;
box-shadow: var(--dc-sh-3);
}
.dc-bs__grab {
display: block; width: 36px; height: 4px;
margin: var(--dc-2) auto 0;
border-radius: var(--dc-r-full);
background: var(--dc-line-bold);
}
.dc-bs__head {
display: flex; align-items: center; gap: var(--dc-2);
padding: var(--dc-3) var(--dc-3) var(--dc-3) var(--dc-5);
}
.dc-bs__title {
flex: 1 1 auto; margin: 0;
font-size: 16px; font-weight: 700; line-height: 1.4;
color: var(--dc-text);
}
.dc-bs__close {
flex: 0 0 auto;
display: grid; place-items: center;
width: 44px; height: 44px;
border: 0; border-radius: var(--dc-r-full);
background: transparent; color: var(--dc-text-sub);
}
.dc-bs__close svg { width: 22px; height: 22px; }
.dc-bs__close:active { background: var(--dc-sunken); }
.dc-bs__body { padding: 0 var(--dc-5) var(--dc-4); }
.dc-bs__body--scroll { overflow-y: auto; -webkit-overflow-scrolling: touch; }
.dc-bs__sec {
margin: var(--dc-4) 0 var(--dc-2);
font-size: 12px; font-weight: 700; letter-spacing: .02em;
color: var(--dc-text-mute);
}
.dc-bs__sec:first-child { margin-top: 0; }
.dc-bs__opt {
display: flex; align-items: center; gap: var(--dc-3);
min-height: 56px; padding: var(--dc-2) 0;
border-bottom: 1px solid var(--dc-line);
cursor: pointer;
}
.dc-bs__opt:last-child { border-bottom: 0; }
.dc-bs__radio {
flex: 0 0 auto;
width: 22px; height: 22px; margin: 0;
accent-color: var(--dc-brand);
}
.dc-bs__opt-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.dc-bs__opt-name { font-size: 14px; font-weight: 600; color: var(--dc-text); }
.dc-bs__opt-desc { font-size: 12px; line-height: 1.5; color: var(--dc-text-sub); }
.dc-bs__opt-price { flex: 0 0 auto; font-size: 13px; font-weight: 700; color: var(--dc-text); }
.dc-bs__radio:checked ~ .dc-bs__opt-main .dc-bs__opt-name { color: var(--dc-brand-text); }
.dc-bs__chips { display: flex; flex-wrap: wrap; gap: var(--dc-2); }
.dc-bs__chip {
position: relative;
display: inline-flex; align-items: center;
min-height: 44px; padding: 0 var(--dc-4);
border: 1px solid var(--dc-line-field); border-radius: var(--dc-r-full);
font-size: 13px; font-weight: 600; color: var(--dc-text-sub);
cursor: pointer;
transition: background var(--dc-fast), border-color var(--dc-fast), color var(--dc-fast);
}
.dc-bs__chip input { position: absolute; width: 1px; height: 1px; opacity: 0; }
.dc-bs__chip input:checked ~ span { color: var(--dc-brand-text); }
.dc-bs__chip:has(input:checked) { border-color: var(--dc-brand); background: var(--dc-brand-soft); }
.dc-bs__chip:focus-within { outline: 2px solid var(--dc-brand); outline-offset: 2px; }
.dc-bs__foot {
display: flex; gap: var(--dc-2);
padding: var(--dc-3) var(--dc-5) calc(var(--dc-4) + var(--dc-safe-bottom));
border-top: 1px solid var(--dc-line);
background: var(--dc-surface);
}
.dc-bs__cta {
flex: 1 1 auto; min-height: 50px; padding: 0 var(--dc-4);
border: 0; border-radius: var(--dc-r-md);
background: var(--dc-brand); color: var(--dc-text-on);
font-size: 15px; font-weight: 700;
transition: background var(--dc-fast);
}
.dc-bs__cta:active { background: var(--dc-brand-hi); }
.dc-bs__ghost {
flex: 0 0 auto; min-width: 92px; min-height: 50px; padding: 0 var(--dc-4);
border: 1px solid var(--dc-line-field); border-radius: var(--dc-r-md);
background: var(--dc-surface); color: var(--dc-text);
font-size: 15px; font-weight: 600;
}
.dc-bs__ghost:active { background: var(--dc-sunken); }
Cách dùng
- Dùng khi nội dung phía sau vẫn cần thấy được; nếu không, hãy chọn modal toàn màn hình
- Quá năm lựa chọn thì chỉ cho phần thân cuộn, giữ tiêu đề và nút xác nhận cố định
- Cho phép chạm ra ngoài để đóng, nhưng hỏi lại nếu sheet đang giữ lựa chọn chưa lưu
- Đừng cao quá 90% màn hình — không thấy gì phía sau thì chẳng khác modal
Điểm cần lưu ý
- Thiếu --dc-safe-bottom ở chân sheet là thanh home che mất nút xác nhận
- Đừng để tay nắm là cách đóng duy nhất — phải có thêm nút đóng
- Khi mở, chuyển tiêu điểm vào sheet và ẩn nội dung phía sau khỏi trình đọc