{
  "id": "motion:motion-hide-on-scroll-header",
  "slug": "motion-hide-on-scroll-header",
  "kind": "motion",
  "title": {
    "ja": "隠れるヘッダー",
    "en": "Hide-on-scroll header"
  },
  "aiName": "Hide-on-scroll header",
  "summary": {
    "ja": "下で隠れ、戻すと出る",
    "en": "Hides when scrolling down and returns on scroll up"
  },
  "number": 23,
  "group": "pin-nav",
  "techniques": [
    "css-transition"
  ],
  "rules": [
    {
      "ja": "下方向で隠し、上方向で出す。方向の判定には 8px のしきい値を置く",
      "en": "Hide on scroll down and reveal on scroll up, deciding direction only past an 8px threshold"
    },
    {
      "ja": "名札の中では出さない。名札を抜けてから初めて現れる",
      "en": "Stay hidden inside the title block; it first appears only after the title block is passed"
    },
    {
      "ja": "動きを減らす設定では JS で隠さず、常に表示したままにする",
      "en": "Under reduced motion, never hide it; keep the header visible"
    }
  ],
  "reproduction": {
    "prompt": {
      "ja": "隠れるヘッダー — Hide-on-scroll header\n\n画面上端に高さ 64px の固定ヘッダーを置く。下へスクロールしたら translateY(-100%) で画面外へ隠し、上へ戻したら translateY(0) で出す。\n方向の判定は、直前に採った位置から差が 8px を超えたときだけ行い、小さな揺れでは切り替えない。\n名札（高さ 72vh）の中にいる間は、方向に関係なく隠したままにする。\n切り替えは 0.7 秒の ease で、CSS と passive な scroll リスナー 1 本だけで実装し、ライブラリは使わない。\n\n外すと崩れる点:\n- 下方向で隠し、上方向で出す。方向の判定には 8px のしきい値を置く\n- 名札の中では出さない。名札を抜けてから初めて現れる\n- 動きを減らす設定では JS で隠さず、常に表示したままにする\n\n動く実物（1 ファイルで完結する HTML）: https://design-vault.pages.dev/designs/motion-hide-on-scroll-header/demo.html",
      "en": "Hide-on-scroll header — Hide-on-scroll header\n\nPlace a fixed 64px header at the top edge. When the user scrolls down, slide it out with translateY(-100%); when they scroll up, bring it back to translateY(0).\nJudge direction only after the position has moved more than 8px from the last accepted position, so small jitter never toggles it.\nWhile the reader is still inside the 72vh title block, keep the header hidden in both directions.\nAnimate over 0.7s with the ease curve, using CSS and a single passive scroll listener; no library.\n\nDo not drop:\n- Hide on scroll down and reveal on scroll up, deciding direction only past an 8px threshold\n- Stay hidden inside the title block; it first appears only after the title block is passed\n- Under reduced motion, never hide it; keep the header visible\n\nA working example (one self-contained HTML file): https://design-vault.pages.dev/designs/motion-hide-on-scroll-header/demo.html"
    },
    "code": {
      "url": "https://design-vault.pages.dev/designs/motion-hide-on-scroll-header/demo.html",
      "html": "<!doctype html>\n<html lang=\"ja\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>23 隠れるヘッダー — Hide-on-scroll header</title>\n<style>\n/* dv-motion-base:start */\n:root {\n  --ground: #FFFFFF;\n  --surface: #F3F5F8;\n  --ink: #14181F;\n  --muted: #5A6472;\n  --accent: #1F5FA8;\n  --rule: #D8DDE4;\n  --accent-soft: color-mix(in srgb, var(--accent) 12%, var(--ground));\n  --accent-mid: color-mix(in srgb, var(--accent) 45%, var(--ground));\n  --shade: color-mix(in srgb, var(--ink) 14%, transparent);\n\n  --font: \"Zen Kaku Gothic New\", \"Hiragino Sans\", \"Noto Sans JP\", \"Yu Gothic\", system-ui, sans-serif;\n  --mono: ui-monospace, \"SF Mono\", Menlo, Consolas, monospace;\n\n  --t-label: 13px;\n  --t-body: 16px;\n  --t-lead: 20px;\n  --t-h2: 32px;\n  --t-h1: clamp(40px, 7vw, 72px);\n\n  --s1: 4px;\n  --s2: 8px;\n  --s3: 16px;\n  --s4: 24px;\n  --s5: 40px;\n  --s6: 64px;\n  --s7: 104px;\n\n  --radius: 12px;\n  --ease: cubic-bezier(.2, .7, .2, 1);\n  --dur: .7s;\n  --measure: 1080px;\n}\n*, *::before, *::after { box-sizing: border-box; }\nhtml { -webkit-text-size-adjust: 100%; }\nbody {\n  margin: 0;\n  background: var(--ground);\n  color: var(--ink);\n  font-family: var(--font);\n  font-size: var(--t-body);\n  line-height: 1.7;\n  font-feature-settings: \"palt\";\n  overflow-x: hidden;\n}\nh1, h2, h3, p, ul, ol, figure { margin: 0; padding: 0; }\nul, ol { list-style: none; }\n.dv-wrap { width: min(100% - 2 * var(--s4), var(--measure)); margin-inline: auto; }\n.dv-label {\n  min-height: 72vh;\n  display: grid;\n  align-content: center;\n  gap: var(--s3);\n  padding-block: var(--s6);\n  border-bottom: 1px solid var(--rule);\n}\n.dv-num { font-family: var(--mono); font-size: var(--t-lead); font-weight: 700; color: var(--accent); letter-spacing: .04em; }\n.dv-name { font-size: var(--t-h1); font-weight: 700; line-height: 1.15; letter-spacing: .01em; }\n.dv-ai { font-family: var(--mono); font-size: var(--t-lead); color: var(--muted); }\n.dv-desc { font-size: var(--t-lead); color: var(--ink); }\n.dv-hint { font-size: var(--t-label); color: var(--muted); letter-spacing: .12em; margin-top: var(--s4); }\n.dv-stage { padding-block: var(--s7); }\n.dv-h2 { font-size: var(--t-h2); font-weight: 700; line-height: 1.3; }\n.dv-lead { font-size: var(--t-lead); color: var(--muted); }\n.dv-eyebrow { font-family: var(--mono); font-size: var(--t-label); color: var(--accent); letter-spacing: .12em; text-transform: uppercase; }\n.dv-card { background: var(--surface); border: 1px solid var(--rule); border-radius: var(--radius); padding: var(--s4); }\n.dv-ph {\n  border-radius: var(--radius);\n  background: linear-gradient(135deg, var(--accent-soft), var(--accent-mid) 60%, var(--accent));\n  aspect-ratio: 16 / 9;\n}\n.dv-ph--alt { background: linear-gradient(200deg, var(--surface), var(--accent-soft) 55%, var(--accent-mid)); }\n.dv-ph--ink { background: linear-gradient(160deg, var(--ink), color-mix(in srgb, var(--ink) 55%, var(--accent))); }\n.dv-end {\n  min-height: 60vh;\n  display: grid;\n  place-items: center;\n  border-top: 1px solid var(--rule);\n  color: var(--muted);\n  font-family: var(--mono);\n  font-size: var(--t-label);\n  letter-spacing: .12em;\n}\n/* dv-motion-base:end */\n\n/* ---- この演出だけの CSS。色は var(--…) か color-mix() のみ ---- */\n.hd-bar {\n  position: fixed;\n  top: 0;\n  left: 0;\n  right: 0;\n  z-index: 10;\n  height: 64px;\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  gap: var(--s3);\n  padding-inline: var(--s4);\n  background: color-mix(in srgb, var(--ground) 92%, transparent);\n  border-bottom: 1px solid var(--rule);\n  transition: transform var(--dur) var(--ease);\n}\n.hd-brand { font-family: var(--mono); font-weight: 700; color: var(--ink); }\n.hd-bar ul { display: flex; gap: var(--s4); font-size: var(--t-label); color: var(--muted); }\n.hd-section { min-height: 80vh; display: grid; align-content: center; }\n.js .hd-bar.is-hidden { transform: translateY(-100%); }\n\n@media (prefers-reduced-motion: reduce) {\n  .hd-bar { transition: none; }\n}\n</style>\n</head>\n<body data-motion-number=\"23\">\n<header class=\"dv-label\">\n  <div class=\"dv-wrap\">\n    <p class=\"dv-num\">23</p>\n    <h1 class=\"dv-name\">隠れるヘッダー</h1>\n    <p class=\"dv-ai\">Hide-on-scroll header</p>\n    <p class=\"dv-desc\">下で隠れ、戻すと出る</p>\n    <p class=\"dv-hint\">SCROLL ↓</p>\n  </div>\n</header>\n\n<main class=\"dv-stage\">\n  <header class=\"hd-bar\">\n    <p class=\"hd-brand\">ロゴ</p>\n    <nav aria-label=\"主要メニュー\">\n      <ul>\n        <li>製品</li>\n        <li>事例</li>\n        <li>料金</li>\n      </ul>\n    </nav>\n  </header>\n  <section class=\"hd-section\">\n    <div class=\"dv-wrap\">\n      <p class=\"dv-eyebrow\">Section 1</p>\n      <h2 class=\"dv-h2\">見出しが入ります</h2>\n      <p class=\"dv-lead\">本文のダミーです。下へ進むとバーが隠れ、上へ戻すと現れます。</p>\n    </div>\n  </section>\n  <section class=\"hd-section\">\n    <div class=\"dv-wrap\">\n      <p class=\"dv-eyebrow\">Section 2</p>\n      <h2 class=\"dv-h2\">見出しが入ります</h2>\n      <p class=\"dv-lead\">本文のダミーです。下へ進むとバーが隠れ、上へ戻すと現れます。</p>\n    </div>\n  </section>\n  <section class=\"hd-section\">\n    <div class=\"dv-wrap\">\n      <p class=\"dv-eyebrow\">Section 3</p>\n      <h2 class=\"dv-h2\">見出しが入ります</h2>\n      <p class=\"dv-lead\">本文のダミーです。下へ進むとバーが隠れ、上へ戻すと現れます。</p>\n    </div>\n  </section>\n  <section class=\"hd-section\">\n    <div class=\"dv-wrap\">\n      <p class=\"dv-eyebrow\">Section 4</p>\n      <h2 class=\"dv-h2\">見出しが入ります</h2>\n      <p class=\"dv-lead\">本文のダミーです。下へ進むとバーが隠れ、上へ戻すと現れます。</p>\n    </div>\n  </section>\n</main>\n\n<footer class=\"dv-end\">END — 23 隠れるヘッダー</footer>\n\n<script>\n/* dv-motion-helpers:start */\nconst dvReduce = matchMedia('(prefers-reduced-motion: reduce)').matches;\ndocument.documentElement.classList.add('js');\n\n/**\n * 画面に入った要素へ .is-in を付ける。\n * data-reveal=\"repeat\" の要素は画面を出たら外す（何度でも再生）。\n */\nfunction dvReveal(selector = '[data-reveal]', { threshold = 0.2, rootMargin = '0px 0px -8% 0px' } = {}) {\n  const els = [...document.querySelectorAll(selector)];\n  if (dvReduce || !('IntersectionObserver' in window)) {\n    els.forEach((el) => el.classList.add('is-in'));\n    return;\n  }\n  const io = new IntersectionObserver((entries) => {\n    for (const e of entries) {\n      if (e.isIntersecting) {\n        e.target.classList.add('is-in');\n        if (e.target.dataset.reveal !== 'repeat') io.unobserve(e.target);\n      } else if (e.target.dataset.reveal === 'repeat') {\n        e.target.classList.remove('is-in');\n      }\n    }\n  }, { threshold, rootMargin });\n  els.forEach((el) => io.observe(el));\n}\n\n/**\n * スクロール量を 0〜1 にして、各 [data-progress] 要素の CSS 変数 --p に書く。\n *   data-progress=\"through\" : 要素の上端が画面の下端に来たとき 0、下端が画面の上端を抜けたとき 1\n *   data-progress=\"pin\"     : 要素の上端が画面の上端に来たとき 0、下端が画面の下端に来たとき 1\n *                             （中に position: sticky の箱を持つ、背の高い区画用）\n *   data-progress=\"page\"    : ページ全体のスクロール量\n * onUpdate(el, p) を渡すと、値が変わるたびに呼ばれる。\n */\nfunction dvProgress(onUpdate) {\n  const els = [...document.querySelectorAll('[data-progress]')];\n  const clamp = (n) => Math.min(1, Math.max(0, n));\n  let queued = false;\n  const measure = () => {\n    queued = false;\n    const vh = window.innerHeight;\n    for (const el of els) {\n      const r = el.getBoundingClientRect();\n      let p;\n      if (el.dataset.progress === 'pin') p = r.height > vh ? -r.top / (r.height - vh) : 0;\n      else if (el.dataset.progress === 'page') {\n        const max = document.documentElement.scrollHeight - vh;\n        p = max > 0 ? window.scrollY / max : 0;\n      } else p = (vh - r.top) / (vh + r.height);\n      p = clamp(p);\n      el.style.setProperty('--p', p.toFixed(4));\n      if (onUpdate) onUpdate(el, p);\n    }\n  };\n  const request = () => {\n    if (!queued) { queued = true; requestAnimationFrame(measure); }\n  };\n  addEventListener('scroll', request, { passive: true });\n  addEventListener('resize', request);\n  measure();\n}\n/* dv-motion-helpers:end */\n\n/* ---- この演出だけの JS ---- */\nconst hdBar = document.querySelector('.hd-bar');\nconst hdLabel = document.querySelector('.dv-label');\nif (!dvReduce) {\n  // 直前に採った位置から 8px 以上動いたときだけ、向きを見て切り替える。\n  // 名札の中（名札の高さより上）は、向きに関係なく隠したままにする。\n  let lastY = window.scrollY;\n  hdBar.classList.add('is-hidden');\n  addEventListener('scroll', () => {\n    const y = window.scrollY;\n    if (Math.abs(y - lastY) < 8) return;\n    const down = y > lastY;\n    lastY = y;\n    hdBar.classList.toggle('is-hidden', down || y < hdLabel.offsetHeight);\n  }, { passive: true });\n}\n</script>\n</body>\n</html>\n"
    }
  },
  "license": "Original",
  "catalogVersion": 1,
  "designSchemaVersion": 2,
  "capabilities": [
    "live-preview",
    "copy-prompt",
    "get-code"
  ],
  "reusePolicy": "construction-reuse",
  "urls": {
    "ja": "https://design-vault.pages.dev/designs/motion-hide-on-scroll-header",
    "en": "https://design-vault.pages.dev/en/designs/motion-hide-on-scroll-header",
    "json": "https://design-vault.pages.dev/ai/assets/motion-hide-on-scroll-header.json",
    "designJson": null
  }
}