{
  "id": "motion:motion-english-underline",
  "slug": "motion-english-underline",
  "kind": "motion",
  "title": {
    "ja": "下に英語",
    "en": "English underneath"
  },
  "aiName": "English subtitle under a title",
  "summary": {
    "ja": "線が引かれ、下に英語が字間を詰めて現れる",
    "en": "A rule is drawn and an English subtitle tightens in beneath the title"
  },
  "number": 59,
  "group": "text-motion",
  "techniques": [
    "scroll-progress"
  ],
  "rules": [
    {
      "ja": "線は scaleX で引く。幅を動かさない",
      "en": "Draw the rule with scaleX, never by animating width"
    },
    {
      "ja": "英語は見出しより小さく薄くして、主役にしない",
      "en": "Keep the English smaller and quieter than the heading so it never leads"
    },
    {
      "ja": "折り返さない。375px でも clamp で収める",
      "en": "Never wrap; fit at 375px with clamp"
    },
    {
      "ja": "動きを減らす設定では線と英語が出た状態で止める",
      "en": "Stop with the rule and the English shown under reduced motion"
    }
  ],
  "reproduction": {
    "prompt": {
      "ja": "下に英語 — English subtitle under a title\n\n日本語の大きな見出しの下に、線を引き、英語の副題を現す動きを作ってください。見出しの直下に見出しと同じ幅の 3px の線を置いて、transform: scaleX(0 → 1)（左端が基点）で引きます。その下の英語はイタリックの明朝（Georgia）で、字間を 0.6em から 0.14em へ詰めながら、不透明度を 0 から 1 にします。340vh の pin 区画で、線は進み 12% から 42%、英語は 30% から 75% で、どちらも easeOut（1 − (1 − t)³）です。英語は見出しの意味を補うだけにして、見出しより小さく、薄い色（var(--muted)）にします。折り返さず、375px でも収まるよう clamp で大きさを決めます。動きを減らす設定では線を引き終え、英語が現れた状態で止めます。CSS と JavaScript だけで実装し、ライブラリは使わないでください。\n\n外すと崩れる点:\n- 線は scaleX で引く。幅を動かさない\n- 英語は見出しより小さく薄くして、主役にしない\n- 折り返さない。375px でも clamp で収める\n- 動きを減らす設定では線と英語が出た状態で止める\n\n動く実物（1 ファイルで完結する HTML）: https://design-vault.pages.dev/designs/motion-english-underline/demo.html",
      "en": "English underneath — English subtitle under a title\n\nUnder a large Japanese heading, draw a rule and bring in an English subtitle. Put a 3px rule of the heading’s width directly beneath it and draw it with transform: scaleX(0 → 1) from the left edge. Set the English below in italic serif (Georgia), tightening its letter-spacing from 0.6em to 0.14em while its opacity goes from 0 to 1. In a 340vh pinned section run the rule from 12% to 42% progress and the English from 30% to 75%, both with ease-out 1 − (1 − t)³. Keep the English supporting, smaller than the heading and in var(--muted). Never wrap, and size with clamp so it fits at 375px. With reduced motion stop with the rule drawn and the English shown. Implement it with CSS and JavaScript only, without any library.\n\nDo not drop:\n- Draw the rule with scaleX, never by animating width\n- Keep the English smaller and quieter than the heading so it never leads\n- Never wrap; fit at 375px with clamp\n- Stop with the rule and the English shown under reduced motion\n\nA working example (one self-contained HTML file): https://design-vault.pages.dev/designs/motion-english-underline/demo.html"
    },
    "code": {
      "url": "https://design-vault.pages.dev/designs/motion-english-underline/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>59 下に英語 — English subtitle under a title</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.stack { display: grid; gap: var(--s6); }\n.eu-zone { height: 340vh; }\n.eu-stick { position: sticky; top: 0; height: 100vh; display: grid; place-content: center; gap: var(--s4); overflow: clip; }\n@media (prefers-reduced-motion: reduce) {\n  .eu-zone { height: auto; }\n  .eu-stick { position: static; height: auto; padding-block: var(--s6); }\n}\n.eu-wrap { width: max-content; max-width: 100%; margin-inline: auto; display: grid; gap: var(--s3); justify-items: center; }\n.eu-title { font-size: clamp(44px, 12vw, 150px); font-weight: 700; line-height: 1.15; letter-spacing: .06em; }\n.eu-rule { width: 100%; height: 3px; background: var(--ink); transform-origin: left center; transform: scaleX(var(--r, 1)); }\n.eu-en { font-family: Georgia, \"Times New Roman\", serif; font-style: italic; font-size: clamp(14px, 2.6vw, 30px); color: var(--muted); letter-spacing: var(--ls, .14em); opacity: var(--o, 1); white-space: nowrap; width: 0; display: flex; justify-content: center; }\n</style>\n</head>\n<body data-motion-number=\"59\">\n<header class=\"dv-label\">\n  <div class=\"dv-wrap\">\n    <p class=\"dv-num\">59</p>\n    <h1 class=\"dv-name\">下に英語</h1>\n    <p class=\"dv-ai\">English subtitle under a title</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<div class=\"dv-wrap stack\">\n    <section class=\"dv-card\">\n      <p class=\"dv-eyebrow\">Section 1</p>\n      <h2 class=\"dv-h2\">見出しが入ります</h2>\n      <p class=\"dv-lead\">本文のダミーです。スクロールすると、見出しの下に線が引かれ、英語の副題が広い字間から詰まりながら現れます。</p>\n    </section>\n  </div>\n\n  <section class=\"eu-zone\" data-progress=\"pin\">\n    <div class=\"eu-stick\">\n      <div class=\"eu-wrap\">\n        <h2 class=\"eu-title\">下に英語</h2>\n        <div class=\"eu-rule\" aria-hidden=\"true\"></div>\n        <p class=\"eu-en\" lang=\"en\">English language placement</p>\n      </div>\n    </div>\n  </section>\n\n  <div class=\"dv-wrap stack\">\n    <section class=\"dv-card\">\n      <p class=\"dv-eyebrow\">Section 2</p>\n      <h2 class=\"dv-h2\">見出しが入ります</h2>\n      <p class=\"dv-lead\">本文のダミーです。英語は、日本語の見出しの意味を補うだけで、主役にはしません。</p>\n    </section>\n  </div>\n</main>\n\n<footer class=\"dv-end\">END — 59 下に英語</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 stage = document.querySelector('.eu-wrap');\nconst clamp = (n) => Math.min(1, Math.max(0, n));\nconst ease = (t) => 1 - Math.pow(1 - t, 3);\nfunction place(p) {\n  const rule = ease(clamp((p - 0.12) / 0.3));\n  const en = ease(clamp((p - 0.3) / 0.45));\n  stage.style.setProperty('--r', rule.toFixed(3));\n  stage.style.setProperty('--o', en.toFixed(3));\n  stage.style.setProperty('--ls', (0.6 - 0.46 * en).toFixed(3) + 'em');   // 0.6em → 0.14em\n}\nif (!dvReduce) dvProgress((el, p) => place(p));\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-english-underline",
    "en": "https://design-vault.pages.dev/en/designs/motion-english-underline",
    "json": "https://design-vault.pages.dev/ai/assets/motion-english-underline.json",
    "designJson": null
  }
}