Đến nội dung chính

Tiêu đề thu gọn khi cuộn

Dùng cho màn hình danh sách: vừa cuộn là tiêu đề lớn gập lại, chỉ còn một thanh mỏng.

Bố cục

Xem trước trực tiếp

Nguồn

<div class="dc-ch">
  <div class="dc-ch__bar">
    <button class="dc-ch__ico" type="button">
      <span aria-hidden="true">‹</span>
      <span class="dc-sr">뒤로</span>
    </button>
    <span class="dc-ch__bar-title" aria-hidden="true">받은 제안</span>
    <button class="dc-ch__ico" type="button">
      <span aria-hidden="true">⋯</span>
      <span class="dc-sr">더보기</span>
    </button>
  </div>
  <div class="dc-ch__hero">
    <h1 class="dc-ch__big">받은 제안</h1>
    <p class="dc-ch__sub">새 제안 3개 · 답장 기다리는 곳 1곳</p>
  </div>
  <ul class="dc-ch__list">
    <li class="dc-ch__item">
      <span class="dc-ch__item-title">대한물류 · 상하차 보조</span>
      <span class="dc-ch__item-meta">인천 · 일당 140,000원 · 어제 받음</span>
    </li>
    <li class="dc-ch__item">
      <span class="dc-ch__item-title">금성정밀 · 용접 보조</span>
      <span class="dc-ch__item-meta">김해 · 일당 160,000원 · 2일 전</span>
    </li>
    <li class="dc-ch__item">
      <span class="dc-ch__item-title">푸른들농장 · 수확 일손</span>
      <span class="dc-ch__item-meta">나주 · 일당 120,000원 · 3일 전</span>
    </li>
    <li class="dc-ch__item">
      <span class="dc-ch__item-title">바다호텔 · 객실 정리</span>
      <span class="dc-ch__item-meta">제주 · 월급 2,450,000원 · 4일 전</span>
    </li>
    <li class="dc-ch__item">
      <span class="dc-ch__item-title">한빛식품 · 포장 라인</span>
      <span class="dc-ch__item-meta">평택 · 일당 130,000원 · 5일 전</span>
    </li>
    <li class="dc-ch__item">
      <span class="dc-ch__item-title">새벽유통 · 분류 작업</span>
      <span class="dc-ch__item-meta">용인 · 일당 135,000원 · 1주 전</span>
    </li>
    <li class="dc-ch__item">
      <span class="dc-ch__item-title">동해조선 · 도장 보조</span>
      <span class="dc-ch__item-meta">통영 · 일당 170,000원 · 1주 전</span>
    </li>
  </ul>
</div>
.dc-ch {
  position: relative;
  height: 520px; overflow-y: auto; overscroll-behavior: contain;
  background: var(--dc-bg);
  border: 1px solid var(--dc-line);
  border-radius: var(--dc-r-md);
  font-family: var(--dc-font);
}
.dc-ch__bar {
  position: sticky; top: 0; z-index: 3;
  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 transparent;
  transition: border-color var(--dc-fast), box-shadow var(--dc-fast);
}
.dc-ch.is-stuck .dc-ch__bar {
  border-bottom-color: var(--dc-line);
  box-shadow: var(--dc-sh-1);
}
.dc-ch__ico {
  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);
  font-size: 22px; line-height: 1;
}
.dc-ch__ico:active { background: var(--dc-sunken); }
.dc-ch__bar-title {
  flex: 1 1 auto; min-width: 0;
  text-align: center; font-size: 16px; font-weight: 700;
  color: var(--dc-text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  opacity: 0; transform: translateY(5px);
  transition: opacity var(--dc-fast), transform var(--dc-fast);
}
.dc-ch.is-stuck .dc-ch__bar-title { opacity: 1; transform: none; }
.dc-ch__hero {
  padding: var(--dc-2) var(--dc-4) var(--dc-5);
  background: var(--dc-surface);
}
.dc-ch__big {
  font-size: 28px; font-weight: 800; letter-spacing: -.03em;
  color: var(--dc-text);
}
.dc-ch__sub { margin-top: var(--dc-1); font-size: 13px; color: var(--dc-text-sub); }
.dc-ch__search {
  width: 100%; min-height: 44px; margin-top: var(--dc-3);
  padding: 0 var(--dc-3);
  border: 1px solid var(--dc-line-field); border-radius: var(--dc-r-sm);
  background: var(--dc-bg); color: var(--dc-text); font-size: 15px;
}
.dc-ch__tabs {
  position: sticky; top: calc(var(--dc-safe-top) + 52px); z-index: 2;
  display: flex; gap: var(--dc-1);
  padding: 0 var(--dc-2);
  background: var(--dc-surface);
  border-bottom: 1px solid var(--dc-line);
}
.dc-ch__tab {
  display: inline-flex; align-items: center;
  min-height: 44px; padding: 0 var(--dc-3);
  border-bottom: 2px solid transparent;
  color: var(--dc-text-mute); font-size: 14px; font-weight: 700;
  text-decoration: none;
}
.dc-ch__tab[aria-current="page"] {
  border-bottom-color: var(--dc-brand);
  color: var(--dc-brand-text);
}
.dc-ch__list {
  display: flex; flex-direction: column;
  padding: 0; background: var(--dc-bg);
}
.dc-ch__item {
  display: flex; flex-direction: column; gap: 2px;
  min-height: 64px; padding: var(--dc-3) var(--dc-4);
  border-bottom: 1px solid var(--dc-line);
}
.dc-ch__item-title { font-size: 15px; font-weight: 700; color: var(--dc-text); }
.dc-ch__item-meta { font-size: 12px; color: var(--dc-text-sub); }
(function () {
  var root = document.querySelector('.dc-ch');
  if (!root) return;
  var sync = function () {
    root.classList.toggle('is-stuck', root.scrollTop > 24);
  };
  root.addEventListener('scroll', sync, { passive: true });
  sync();
})();

Cách dùng

  • Cho tiêu đề lớn cuộn cùng nội dung, chỉ dán sticky phần thanh mỏng
  • Trong thanh mỏng chỉ giữ thứ luôn phải bấm được, như nút quay lại
  • Khi đã gập, hiện lại đúng tiêu đề đó trên thanh để người dùng không mất dấu

Điểm cần lưu ý

  • Không có JavaScript thì tiêu đề lớn và danh sách vẫn phải hiện; chỉ tiêu đề nhỏ đến muộn
  • Chỉ chuyển động opacity và transform, đừng chuyển động height — cuộn sẽ bị giật
  • Thanh đã gập vẫn phải cao đủ 44px để bấm

Tiếp theo