Product detail (sticky buy bar)
The screen where someone decides yes or no — price, arrival date and options have to sit where the thumb already is.
Live preview
Source
<script>document.body.className="dc-bleed"</script>
<div class="dc-pd">
<header class="dc-pd__bar">
<a class="dc-pd__ic-btn" href="#dc-pd-back">
<svg class="dc-pd__ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 5l-7 7 7 7"/></svg>
<span class="dc-sr">뒤로</span>
</a>
<span class="dc-pd__bar-t">성주 꿀참외</span>
<a class="dc-pd__ic-btn" href="#dc-pd-share">
<svg class="dc-pd__ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 15V4"/><path d="M8 8l4-4 4 4"/><path d="M5 13v6h14v-6"/></svg>
<span class="dc-sr">공유하기</span>
</a>
<a class="dc-pd__ic-btn" href="#dc-pd-cart">
<svg class="dc-pd__ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="9" cy="20" r="1.6"/><circle cx="18" cy="20" r="1.6"/><path d="M3 4h2l2.6 11h11L21 7H6"/></svg>
<span class="dc-sr">장바구니</span>
</a>
</header>
<main class="dc-pd__body">
<div class="dc-pd__hero">
<span class="dc-pd__img" aria-hidden="true"></span>
<span class="dc-pd__page">사진 1 / 5</span>
</div>
<section class="dc-pd__info">
<p class="dc-pd__brand">마켓온 산지직송</p>
<h1 class="dc-pd__name">성주 꿀참외 1.5kg (5~7과)</h1>
<a class="dc-pd__rate" href="#dc-pd-review">
<svg class="dc-pd__star" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 4l2.4 5 5.6.7-4 3.9 1 5.4-5-2.7-5 2.7 1-5.4-4-3.9 5.6-.7z"/></svg>
<strong>4.7</strong>
<span class="dc-pd__rate-n">후기 842개</span>
</a>
<p class="dc-pd__price">
<span class="dc-pd__off">22%</span>
<s class="dc-pd__was">32,000원</s>
<strong class="dc-pd__now">24,900원</strong>
</p>
<p class="dc-pd__point">구매 시 249원 적립 · 카드 즉시할인 최대 2,000원</p>
</section>
<ul class="dc-pd__facts">
<li class="dc-pd__fact">
<span class="dc-pd__fact-k">배송</span>
<span class="dc-pd__fact-v">내일(9월 8일 화) 오전 7시 전 도착<br><span class="dc-pd__fact-note">밤 11시까지 주문 · 15,000원 이상 무료</span></span>
</li>
<li class="dc-pd__fact">
<span class="dc-pd__fact-k">보관</span>
<span class="dc-pd__fact-v">냉장 2~6도 · 받은 뒤 5일 안에 드시길 권합니다</span>
</li>
<li class="dc-pd__fact">
<span class="dc-pd__fact-k">산지</span>
<span class="dc-pd__fact-v">경상북도 성주 · 당도 13Brix 이상만 골라 담습니다</span>
</li>
</ul>
<section class="dc-pd__opt">
<label class="dc-pd__opt-lab" for="dc-pd-size">중량 선택</label>
<select class="dc-pd__select" id="dc-pd-size" name="size">
<option value="1.5">1.5kg (5~7과) · 24,900원</option>
<option value="3">3kg (10~14과) · 44,900원</option>
<option value="5">5kg (17~22과) · 69,900원</option>
</select>
</section>
<section class="dc-pd__sec" id="dc-pd-review">
<h2 class="dc-pd__h2">후기 842개 · 별점 4.7</h2>
<div class="dc-pd__review">
<p class="dc-pd__review-head">
<span class="dc-pd__avatar" aria-hidden="true">이</span>
<span class="dc-pd__review-who">이수민<span class="dc-pd__review-date">2026년 9월 2일 · 재구매</span></span>
<span class="dc-pd__review-score">별점 5.0</span>
</p>
<p class="dc-pd__review-text">아침 6시 40분에 문 앞에 와 있었어요. 일곱 개 중 흠 난 것이 하나도 없고 향이 진합니다. 아이가 한 통을 앉은자리에서 다 먹었습니다.</p>
</div>
<a class="dc-pd__more" href="#dc-pd-review">후기 842개 전체 보기</a>
</section>
</main>
<div class="dc-pd__buy">
<label class="dc-pd__fav" for="dc-pd-fav">
<input class="dc-pd__fav-in" id="dc-pd-fav" type="checkbox">
<svg class="dc-pd__heart" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 20s-7-4.4-7-9.3A4.2 4.2 0 0 1 12 7a4.2 4.2 0 0 1 7 3.7C19 15.6 12 20 12 20z"/></svg>
<span class="dc-sr">찜하기</span>
</label>
<a class="dc-pd__cart" href="#dc-pd-addcart">장바구니</a>
<a class="dc-pd__order" href="#dc-pd-order">바로 구매</a>
</div>
</div>
.dc-pd { display:flex; flex-direction:column; min-height:100vh; background:var(--dc-bg); color:var(--dc-text); font-family:var(--dc-font); }
.dc-pd__bar { position:sticky; top:0; z-index:4; display:flex; align-items:center; gap:var(--dc-1); padding:calc(var(--dc-safe-top) + var(--dc-1)) var(--dc-2) var(--dc-1); background:var(--dc-surface); border-bottom:1px solid var(--dc-line); }
.dc-pd__ic-btn { display:inline-flex; align-items:center; justify-content:center; width:44px; height:44px; flex:none; border-radius:var(--dc-r-full); color:var(--dc-text); text-decoration:none; }
.dc-pd__ic-btn:active { background:var(--dc-sunken); }
.dc-pd__ic { width:22px; height:22px; }
.dc-pd__bar-t { flex:1; min-width:0; font-size:15px; font-weight:700; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.dc-pd__body { flex:1; }
.dc-pd__hero { position:relative; }
.dc-pd__img { display:block; width:100%; aspect-ratio:1/1; background:linear-gradient(150deg, var(--dc-warn-soft), var(--dc-sunken) 72%); }
.dc-pd__page { position:absolute; right:var(--dc-3); bottom:var(--dc-3); padding:var(--dc-1) var(--dc-3); border-radius:var(--dc-r-full); background:var(--dc-surface); color:var(--dc-text-sub); font-size:12px; font-weight:600; box-shadow:var(--dc-sh-1); }
.dc-pd__info { padding:var(--dc-5) var(--dc-4) var(--dc-4); }
.dc-pd__brand { font-size:13px; color:var(--dc-text-sub); }
.dc-pd__name { margin-top:var(--dc-1); font-size:20px; font-weight:800; line-height:1.35; letter-spacing:-.02em; }
.dc-pd__rate { display:inline-flex; align-items:center; gap:var(--dc-1); min-height:44px; font-size:14px; color:var(--dc-text); text-decoration:none; }
.dc-pd__star { width:16px; height:16px; color:var(--dc-warn); fill:currentColor; }
.dc-pd__rate-n { margin-left:var(--dc-1); color:var(--dc-text-sub); text-decoration:underline; text-underline-offset:.2em; }
.dc-pd__price { display:flex; align-items:baseline; flex-wrap:wrap; gap:var(--dc-2); }
.dc-pd__off { color:var(--dc-danger); font-size:20px; font-weight:800; }
.dc-pd__was { color:var(--dc-text-mute); font-size:14px; }
.dc-pd__now { font-size:25px; font-weight:800; letter-spacing:-.02em; }
.dc-pd__point { margin-top:var(--dc-2); font-size:13px; color:var(--dc-brand-text); }
.dc-pd__facts { margin:0 var(--dc-4); padding:var(--dc-4) 0; border-top:1px solid var(--dc-line); border-bottom:1px solid var(--dc-line); }
.dc-pd__fact { display:flex; gap:var(--dc-4); padding:var(--dc-1) 0; font-size:14px; line-height:1.5; }
.dc-pd__fact-k { width:40px; flex:none; color:var(--dc-text-mute); font-size:13px; }
.dc-pd__fact-v { flex:1; min-width:0; }
.dc-pd__fact-note { color:var(--dc-text-mute); font-size:12px; }
.dc-pd__opt { padding:var(--dc-5) var(--dc-4) 0; }
.dc-pd__opt-lab { display:block; margin-bottom:var(--dc-2); font-size:13px; font-weight:700; }
.dc-pd__select { width:100%; min-height:52px; padding:0 var(--dc-3); border:1px solid var(--dc-line-field); border-radius:var(--dc-r-sm); background:var(--dc-surface); color:var(--dc-text); font-size:15px; }
.dc-pd__sec { padding:var(--dc-6) var(--dc-4) var(--dc-7); }
.dc-pd__h2 { margin-bottom:var(--dc-3); font-size:16px; font-weight:800; }
.dc-pd__review { padding:var(--dc-4); border-radius:var(--dc-r-md); background:var(--dc-sunken); }
.dc-pd__review-head { display:flex; align-items:center; gap:var(--dc-2); }
.dc-pd__avatar { display:inline-flex; align-items:center; justify-content:center; width:32px; height:32px; flex:none; border-radius:var(--dc-r-full); background:var(--dc-brand-soft); color:var(--dc-brand-text); font-size:13px; font-weight:700; }
.dc-pd__review-who { flex:1; display:flex; flex-direction:column; min-width:0; font-size:13px; font-weight:700; }
.dc-pd__review-date { color:var(--dc-text-mute); font-size:11px; font-weight:400; }
.dc-pd__review-score { color:var(--dc-warn); font-size:12px; font-weight:700; white-space:nowrap; }
.dc-pd__review-text { margin-top:var(--dc-3); font-size:14px; line-height:1.65; color:var(--dc-text-sub); }
.dc-pd__more { display:flex; align-items:center; justify-content:center; min-height:48px; margin-top:var(--dc-3); border:1px solid var(--dc-line-field); border-radius:var(--dc-r-sm); font-size:14px; font-weight:600; color:var(--dc-text); text-decoration:none; }
.dc-pd__buy { position:sticky; bottom:0; z-index:4; display:flex; align-items:center; gap:var(--dc-2); padding:var(--dc-2) var(--dc-3) calc(var(--dc-safe-bottom) + var(--dc-2)); background:var(--dc-surface); border-top:1px solid var(--dc-line); }
.dc-pd__fav { position:relative; display:inline-flex; align-items:center; justify-content:center; width:48px; height:52px; flex:none; border:1px solid var(--dc-line-field); border-radius:var(--dc-r-md); color:var(--dc-text-sub); cursor:pointer; }
.dc-pd__fav-in { appearance:none; position:absolute; inset:0; width:100%; height:100%; margin:0; padding:0; border:0; border-radius:var(--dc-r-md); background:none; }
.dc-pd__heart { width:24px; height:24px; }
.dc-pd__fav-in:checked ~ .dc-pd__heart { color:var(--dc-danger); fill:currentColor; }
.dc-pd__cart { display:flex; align-items:center; justify-content:center; flex:1; min-height:52px; border:1px solid var(--dc-brand); border-radius:var(--dc-r-md); background:var(--dc-surface); color:var(--dc-brand-text); font-size:15px; font-weight:700; text-decoration:none; }
.dc-pd__order { display:flex; align-items:center; justify-content:center; flex:1.4; min-height:52px; border-radius:var(--dc-r-md); background:var(--dc-brand); color:var(--dc-text-on); font-size:15px; font-weight:700; text-decoration:none; }
How to use it
- Pin the buy bar to the bottom; the moment reading turns into deciding, the thumb is already there
- Emphasize one action — buy now — and keep add-to-cart a step quieter
- Write arrival as a date, 'before 7am tomorrow, Sep 8', never as 'fast delivery'
- When the option changes the price, print the amount inside the option itself
Watch out for
- Without --dc-safe-bottom the home indicator eats into the buy bar
- Make the favorite a real checkbox so its pressed state is announced, not just colored in
- Even with one option, give the select a <label>; placeholder text is not a name