Created
September 6, 2026 01:14
-
-
Save link2004/e21645cdb70ea79bc0f7f00078e364da to your computer and use it in GitHub Desktop.
YouLet Spark loop interactive demos
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="ja"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" /> | |
| <title>YouLet Spark Loop Demos</title> | |
| <style> | |
| :root { | |
| --bg: #0b0b0f; | |
| --card: #16161d; | |
| --line: #2a2a35; | |
| --text: #f4f4f7; | |
| --muted: #9a9aab; | |
| --accent: #ff5a7a; | |
| --accent2: #7c6cff; | |
| --good: #3dd68c; | |
| --warn: #ffb020; | |
| --phone: #111118; | |
| --safe: #1c1c24; | |
| } | |
| * { box-sizing: border-box; } | |
| html, body { margin: 0; padding: 0; background: var(--bg); color: var(--text); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Hiragino Sans", "Noto Sans JP", sans-serif; } | |
| body { min-height: 100vh; } | |
| header { | |
| padding: 28px 20px 8px; | |
| max-width: 1100px; | |
| margin: 0 auto; | |
| } | |
| header h1 { font-size: 22px; margin: 0 0 6px; letter-spacing: -0.02em; } | |
| header p { margin: 0; color: var(--muted); font-size: 14px; line-height: 1.5; } | |
| .tabs { | |
| display: flex; gap: 8px; overflow-x: auto; padding: 16px 20px 8px; | |
| max-width: 1100px; margin: 0 auto; scrollbar-width: none; | |
| } | |
| .tabs::-webkit-scrollbar { display: none; } | |
| .tab { | |
| flex: 0 0 auto; border: 1px solid var(--line); background: var(--card); | |
| color: var(--text); border-radius: 999px; padding: 10px 14px; font-size: 13px; | |
| cursor: pointer; white-space: nowrap; | |
| } | |
| .tab.active { background: linear-gradient(135deg, var(--accent), var(--accent2)); border-color: transparent; } | |
| .layout { | |
| max-width: 1100px; margin: 0 auto; padding: 8px 20px 40px; | |
| display: grid; grid-template-columns: 360px 1fr; gap: 24px; align-items: start; | |
| } | |
| @media (max-width: 860px) { | |
| .layout { grid-template-columns: 1fr; } | |
| .phone-wrap { justify-content: center; } | |
| } | |
| .phone-wrap { display: flex; justify-content: flex-start; } | |
| .phone { | |
| width: 340px; height: 700px; background: var(--phone); | |
| border: 1px solid var(--line); border-radius: 36px; overflow: hidden; | |
| position: relative; box-shadow: 0 20px 60px rgba(0,0,0,.45); | |
| } | |
| .notch { | |
| position: absolute; top: 10px; left: 50%; transform: translateX(-50%); | |
| width: 110px; height: 24px; background: #000; border-radius: 20px; z-index: 5; | |
| } | |
| .screen { height: 100%; display: flex; flex-direction: column; background: #0e0e14; } | |
| .status { | |
| padding: 42px 18px 10px; display: flex; justify-content: space-between; align-items: center; | |
| font-size: 12px; color: var(--muted); | |
| } | |
| .status strong { color: var(--text); font-size: 14px; } | |
| .content { flex: 1; overflow: auto; padding: 8px 16px 16px; } | |
| .spark-card { | |
| background: linear-gradient(160deg, #1a1a24, #12121a); | |
| border: 1px solid var(--line); border-radius: 20px; padding: 16px; margin-bottom: 12px; | |
| } | |
| .spark-card .label { | |
| display: inline-flex; align-items: center; gap: 6px; | |
| font-size: 11px; color: var(--accent); letter-spacing: .08em; text-transform: uppercase; margin-bottom: 10px; | |
| } | |
| .spark-card .who { display: flex; gap: 10px; align-items: center; margin-bottom: 12px; } | |
| .avatar { | |
| width: 40px; height: 40px; border-radius: 14px; | |
| background: linear-gradient(135deg, #ff7a93, #7c6cff); display:grid; place-items:center; font-weight:700; font-size:14px; | |
| } | |
| .avatar.b { background: linear-gradient(135deg, #56d4ff, #7c6cff); } | |
| .spark-card h3 { margin: 0; font-size: 15px; } | |
| .spark-card .meta { font-size: 12px; color: var(--muted); } | |
| .spark-text { font-size: 15px; line-height: 1.55; margin: 0 0 14px; } | |
| .photo { | |
| height: 160px; border-radius: 16px; margin-bottom: 12px; | |
| background: #222; position: relative; overflow: hidden; | |
| display:flex; align-items:flex-end; padding: 12px; color: white; font-size: 12px; | |
| } | |
| .photo.p1 { background: linear-gradient(160deg,#3a2a1a,#1a120c), url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="300" height="160"><rect fill="%233a2a1a" width="300" height="160"/><circle cx="220" cy="50" r="28" fill="%23ffb84d"/><rect x="0" y="100" width="300" height="60" fill="%23221810"/></svg>'); background-size: cover; } | |
| .photo.p2 { background: linear-gradient(160deg,#1a2a3a,#0c121a); } | |
| .photo.p3 { background: linear-gradient(160deg,#2a1a3a,#120c1a); } | |
| .photo.grid { display:grid; grid-template-columns:1fr 1fr; gap:6px; height:140px; padding:6px; align-items:stretch; } | |
| .photo.grid > div { border-radius:12px; display:flex; align-items:flex-end; padding:8px; font-size:11px; } | |
| .chip-row { display:flex; flex-wrap:wrap; gap:8px; margin-bottom: 12px; } | |
| .chip { | |
| border: 1px solid var(--line); background: var(--safe); color: var(--text); | |
| border-radius: 999px; padding: 8px 12px; font-size: 13px; cursor: pointer; | |
| } | |
| .chip:active, .chip.selected { border-color: var(--accent); background: #2a1520; } | |
| .cta { | |
| width: 100%; border: 0; border-radius: 14px; padding: 14px 16px; font-size: 15px; font-weight: 650; | |
| cursor: pointer; background: linear-gradient(135deg, var(--accent), #ff7d55); color: white; | |
| } | |
| .cta.secondary { background: var(--safe); border: 1px solid var(--line); color: var(--text); margin-top: 8px; } | |
| .cta:disabled { opacity: .45; cursor: default; } | |
| .composer { | |
| display: none; gap: 8px; margin-top: 8px; | |
| } | |
| .composer.show { display: flex; } | |
| .composer input { | |
| flex: 1; border-radius: 12px; border: 1px solid var(--line); background: #0a0a10; | |
| color: var(--text); padding: 12px; font-size: 14px; | |
| } | |
| .toast { | |
| position: absolute; left: 16px; right: 16px; bottom: 24px; | |
| background: rgba(20,20,28,.96); border: 1px solid var(--line); border-radius: 14px; | |
| padding: 12px 14px; font-size: 13px; display:none; z-index: 6; | |
| } | |
| .toast.show { display:block; animation: up .25s ease; } | |
| @keyframes up { from { transform: translateY(8px); opacity: 0;} to { transform:none; opacity:1;} } | |
| .panel { | |
| background: var(--card); border: 1px solid var(--line); border-radius: 20px; padding: 18px; | |
| } | |
| .panel h2 { margin: 0 0 8px; font-size: 18px; } | |
| .panel .hyp { color: var(--muted); font-size: 13px; line-height: 1.55; margin-bottom: 14px; } | |
| .metric { | |
| display:grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-bottom: 14px; | |
| } | |
| .metric div { | |
| background: var(--safe); border-radius: 12px; padding: 10px; text-align:center; | |
| } | |
| .metric b { display:block; font-size: 18px; } | |
| .metric span { font-size: 11px; color: var(--muted); } | |
| .log { | |
| background: #0a0a10; border-radius: 12px; padding: 12px; min-height: 120px; | |
| font-size: 13px; color: var(--muted); line-height: 1.5; white-space: pre-wrap; | |
| } | |
| .tag { | |
| display:inline-block; font-size:11px; padding: 3px 8px; border-radius: 999px; margin-right:6px; | |
| background:#241820; color:#ff9db0; border:1px solid #3a2430; | |
| } | |
| .tag.alt { background:#181828; color:#b7aeff; border-color:#2c2a44; } | |
| .steps { display:flex; flex-direction:column; gap:8px; margin: 12px 0 0; } | |
| .step { | |
| display:flex; gap:10px; align-items:flex-start; font-size:13px; color: var(--muted); | |
| } | |
| .step i { | |
| width: 20px; height: 20px; border-radius: 50%; background: #222; display:grid; place-items:center; | |
| font-style: normal; font-size: 11px; color: var(--text); flex: 0 0 auto; | |
| } | |
| .step.done { color: var(--good); } | |
| .step.done i { background: #163526; } | |
| .hidden { display:none !important; } | |
| .shutter { | |
| width: 64px; height: 64px; border-radius: 50%; border: 4px solid #fff; background: #ff5a7a; | |
| margin: 18px auto 0; display:block; cursor: pointer; | |
| } | |
| .cam-ui { text-align:center; padding-top: 20px; } | |
| .cam-ui .viewfinder { | |
| height: 280px; border-radius: 18px; background: linear-gradient(160deg,#2a2a20,#10100c); | |
| margin-bottom: 8px; display:grid; place-items:center; color: var(--muted); font-size: 13px; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <header> | |
| <h1>YouLet Spark — ループ案デモ</h1> | |
| <p>仮説切り分け用。触ると右にイベントが流れる。「写真返信が本命か/物語が薄いか」を体感で比較する。</p> | |
| </header> | |
| <div class="tabs" id="tabs"></div> | |
| <div class="layout"> | |
| <div class="phone-wrap"> | |
| <div class="phone"> | |
| <div class="notch"></div> | |
| <div class="screen" id="screen"></div> | |
| <div class="toast" id="toast"></div> | |
| </div> | |
| </div> | |
| <div class="panel"> | |
| <div id="meta"></div> | |
| <div class="metric" id="metrics"> | |
| <div><b id="m-open">0</b><span>開封</span></div> | |
| <div><b id="m-act">0</b><span>行動</span></div> | |
| <div><b id="m-loop">0</b><span>ループ完走</span></div> | |
| </div> | |
| <div class="log" id="log">操作するとここに流れます</div> | |
| </div> | |
| </div> | |
| <script> | |
| const demos = [ | |
| { | |
| id: 'current', | |
| tab: 'いまのループ', | |
| title: '友達の瞬間 → 写真で返す', | |
| tag: '現行', | |
| tagClass: '', | |
| hyp: '仮説: 開封後の摩擦が高い。カメラを近づければ返信率が上がる。', | |
| measure: '開封 → 写真投稿', | |
| render(root, api) { | |
| root.innerHTML = ` | |
| <div class="status"><strong>Spark</strong><span>いま</span></div> | |
| <div class="content"> | |
| <div class="spark-card" id="card"> | |
| <div class="label">✦ Spark</div> | |
| <div class="who"> | |
| <div class="avatar">H</div> | |
| <div><h3>Hinata</h3><div class="meta">Mountain View · 夕方</div></div> | |
| </div> | |
| <div class="photo p1">夕焼けの散歩</div> | |
| <p class="spark-text">Anna、HinataがMountain Viewで友達と夕焼けを歩いてたよ。いい空気感。</p> | |
| <button class="cta" id="open">開く</button> | |
| <button class="cta secondary hidden" id="reply">写真を撮って返す</button> | |
| </div> | |
| <div class="cam-ui hidden" id="cam"> | |
| <div class="viewfinder">裏カメラ · 今の景色を1枚</div> | |
| <button class="shutter" id="shot" title="撮影"></button> | |
| <button class="cta secondary" id="cancel">やめる</button> | |
| </div> | |
| </div>`; | |
| const open = root.querySelector('#open'); | |
| const reply = root.querySelector('#reply'); | |
| const cam = root.querySelector('#cam'); | |
| const card = root.querySelector('#card'); | |
| open.onclick = () => { | |
| api.open(); | |
| open.classList.add('hidden'); | |
| reply.classList.remove('hidden'); | |
| api.log('開封した(中身を読んだ)'); | |
| }; | |
| reply.onclick = () => { | |
| card.classList.add('hidden'); | |
| cam.classList.remove('hidden'); | |
| api.log('カメラ導線へ'); | |
| }; | |
| root.querySelector('#cancel').onclick = () => { | |
| cam.classList.add('hidden'); | |
| card.classList.remove('hidden'); | |
| api.log('撮影をやめた → 摩擦で離脱'); | |
| }; | |
| root.querySelector('#shot').onclick = () => { | |
| api.act('photo'); | |
| api.loop(); | |
| api.toast('写真を送った。相手に届く想定'); | |
| api.log('写真返信 → ループ完走(相手還流は未確認)'); | |
| cam.classList.add('hidden'); | |
| card.classList.remove('hidden'); | |
| reply.classList.add('hidden'); | |
| open.classList.remove('hidden'); | |
| open.textContent = 'もう一度試す'; | |
| }; | |
| } | |
| }, | |
| { | |
| id: 'react', | |
| tab: 'リアクション完了', | |
| title: '知らせるだけ → 一言/リアクション', | |
| tag: '変種1 · はな型', | |
| tagClass: 'alt', | |
| hyp: '仮説H文脈/ループ: 写真必須が間違い。反応できればループは回る。', | |
| measure: '開封 → リアクション or 一言', | |
| render(root, api) { | |
| root.innerHTML = ` | |
| <div class="status"><strong>Spark</strong><span>リアクションで完了</span></div> | |
| <div class="content"> | |
| <div class="spark-card"> | |
| <div class="label">✦ Spark</div> | |
| <div class="who"><div class="avatar">H</div><div><h3>Hinata</h3><div class="meta">Mountain View</div></div></div> | |
| <div class="photo p1">夕焼けの散歩</div> | |
| <p class="spark-text">Anna、Hinataが夕焼け歩いてたよ。</p> | |
| <button class="cta" id="open">開く</button> | |
| <div id="after" class="hidden"> | |
| <div class="chip-row"> | |
| <button class="chip" data-r="わかる">わかる</button> | |
| <button class="chip" data-r="いいな">いいな</button> | |
| <button class="chip" data-r="写真見たい">写真見たい</button> | |
| </div> | |
| <div class="composer" id="comp"> | |
| <input id="msg" placeholder="一言で返す(任意)" /> | |
| <button class="cta" id="send" style="width:auto;padding:12px 14px">送る</button> | |
| </div> | |
| <button class="cta secondary" id="showComp">一言を書く</button> | |
| </div> | |
| </div> | |
| </div>`; | |
| root.querySelector('#open').onclick = (e) => { | |
| api.open(); | |
| e.target.classList.add('hidden'); | |
| root.querySelector('#after').classList.remove('hidden'); | |
| api.log('開封'); | |
| }; | |
| root.querySelectorAll('.chip').forEach(btn => { | |
| btn.onclick = () => { | |
| api.act('react:' + btn.dataset.r); | |
| api.loop(); | |
| api.toast(`「${btn.dataset.r}」で完了。写真なし`); | |
| api.log('リアクション完了 → 写真必須を外したループ'); | |
| }; | |
| }); | |
| root.querySelector('#showComp').onclick = () => { | |
| root.querySelector('#comp').classList.add('show'); | |
| api.log('一言入力を開いた'); | |
| }; | |
| root.querySelector('#send').onclick = () => { | |
| const v = root.querySelector('#msg').value.trim() || '(空の一言)'; | |
| api.act('one-liner'); | |
| api.loop(); | |
| api.toast('一言を送った'); | |
| api.log('一言返信: ' + v); | |
| }; | |
| } | |
| }, | |
| { | |
| id: 'shared', | |
| tab: '共通の話題', | |
| title: '二人の写真から一点を作る', | |
| tag: '変種2', | |
| tagClass: 'alt', | |
| hyp: '仮説H文脈: 片方向の「友達がXしてた」より、二人に共通する種の方が会話が始まる。', | |
| measure: '開封 → 会話シードへの反応', | |
| render(root, api) { | |
| root.innerHTML = ` | |
| <div class="status"><strong>Spark</strong><span>共通点</span></div> | |
| <div class="content"> | |
| <div class="spark-card"> | |
| <div class="label">✦ 二人のあいだ</div> | |
| <div class="photo grid"> | |
| <div class="p1">Anna · ピクニック</div> | |
| <div class="p2" style="background:linear-gradient(160deg,#1a2a3a,#0c121a)">Hinata · 夕焼け</div> | |
| </div> | |
| <p class="spark-text">今週、二人とも「外でゆっくりする時間」撮ってる。AnnaのピクニックとHinataの散歩、どっちの空気が好き?</p> | |
| <button class="cta" id="open">開く</button> | |
| <div id="after" class="hidden"> | |
| <div class="chip-row"> | |
| <button class="chip" data-r="picnic">ピクニック派</button> | |
| <button class="chip" data-r="walk">夕焼け散歩派</button> | |
| <button class="chip" data-r="both">どっちも欲しい</button> | |
| </div> | |
| <button class="cta secondary" id="ask">Hinataに聞く</button> | |
| </div> | |
| </div> | |
| </div>`; | |
| root.querySelector('#open').onclick = (e) => { | |
| api.open(); e.target.classList.add('hidden'); | |
| root.querySelector('#after').classList.remove('hidden'); | |
| api.log('共通話題Sparkを開封'); | |
| }; | |
| root.querySelectorAll('.chip').forEach(btn => btn.onclick = () => { | |
| api.act('pick:' + btn.dataset.r); | |
| api.loop(); | |
| api.toast('好みを残した。次は相手の画面に種が渡る'); | |
| api.log('共通話題に投票 → 会話シード'); | |
| }); | |
| root.querySelector('#ask').onclick = () => { | |
| api.act('ask-friend'); | |
| api.loop(); | |
| api.toast('Hinataに質問を投げた'); | |
| api.log('相手への問いとして還流開始'); | |
| }; | |
| } | |
| }, | |
| { | |
| id: 'now', | |
| tab: '受信者の今', | |
| title: '相手の話 × あなたの今', | |
| tag: '変種3 · 文脈増強', | |
| tagClass: 'alt', | |
| hyp: '仮説H文脈: 写真の情報次元不足を、受信者側の今(場所・直近投稿・開いてる事実)で補う。', | |
| measure: '開封 → 自分の今に紐づく行動', | |
| render(root, api) { | |
| root.innerHTML = ` | |
| <div class="status"><strong>Spark</strong><span>あなたの今</span></div> | |
| <div class="content"> | |
| <div class="spark-card"> | |
| <div class="label">✦ For you right now</div> | |
| <div class="who"><div class="avatar b">R</div><div><h3>Rosie</h3><div class="meta">SF · ピクニック</div></div></div> | |
| <div class="photo p3" style="background:linear-gradient(160deg,#3a2a40,#141018)">餃子とクレームブリュレ</div> | |
| <p class="spark-text">Anna、いまアプリ開いてるよね。RosieがSFでピクニックしてた。あなたも直近「編集作業の写真」上げてたから、外の空気とどっちが今ほしい?</p> | |
| <div class="chip-row" style="margin-top:-4px"> | |
| <span class="chip" style="cursor:default;opacity:.8">コンテキスト: 開いている</span> | |
| <span class="chip" style="cursor:default;opacity:.8">直近: 編集作業</span> | |
| </div> | |
| <button class="cta" id="open">開く</button> | |
| <div id="after" class="hidden"> | |
| <button class="cta" id="out">外の写真を1枚足す</button> | |
| <button class="cta secondary" id="desk">作業続きを共有</button> | |
| <button class="cta secondary" id="skip">今は見だけ</button> | |
| </div> | |
| </div> | |
| </div>`; | |
| root.querySelector('#open').onclick = (e) => { | |
| api.open(); e.target.classList.add('hidden'); | |
| root.querySelector('#after').classList.remove('hidden'); | |
| api.log('受信者コンテキスト付きSparkを開封'); | |
| }; | |
| root.querySelector('#out').onclick = () => { | |
| api.act('add-outside'); api.loop(); | |
| api.toast('自分の今を足した'); | |
| api.log('受信者の今に紐づけて投稿'); | |
| }; | |
| root.querySelector('#desk').onclick = () => { | |
| api.act('share-desk'); api.loop(); | |
| api.toast('作業側の今を共有'); | |
| api.log('対比で自分の文脈を返した'); | |
| }; | |
| root.querySelector('#skip').onclick = () => { | |
| api.log('見るだけで終了 → 文脈があっても行動しないケース'); | |
| api.toast('未行動で閉じた'); | |
| }; | |
| } | |
| }, | |
| { | |
| id: 'question', | |
| tab: '質問で終わる', | |
| title: '撮る → 答える に変える', | |
| tag: '変種4', | |
| tagClass: 'alt', | |
| hyp: '仮説ループ変更: 行動コストを「撮影」から「答えやすい一文」に落とす。', | |
| measure: '開封 → 回答', | |
| render(root, api) { | |
| root.innerHTML = ` | |
| <div class="status"><strong>Spark</strong><span>質問</span></div> | |
| <div class="content"> | |
| <div class="spark-card"> | |
| <div class="label">✦ 聞いてみた</div> | |
| <div class="who"><div class="avatar">H</div><div><h3>Hinata</h3><div class="meta">夕焼け</div></div></div> | |
| <div class="photo p1">夕焼けの散歩</div> | |
| <p class="spark-text">Anna、Hinataが夕焼け歩いてた。あなたなら今、誰と歩きたい?</p> | |
| <button class="cta" id="open">開く</button> | |
| <div id="after" class="hidden"> | |
| <div class="chip-row"> | |
| <button class="chip" data-a="ひとりで">ひとりで</button> | |
| <button class="chip" data-a="Hinataと">Hinataと</button> | |
| <button class="chip" data-a="Rosieと">Rosieと</button> | |
| </div> | |
| <div class="composer show"> | |
| <input id="msg" placeholder="自由に一言" /> | |
| <button class="cta" id="send" style="width:auto;padding:12px 14px">答える</button> | |
| </div> | |
| </div> | |
| </div> | |
| </div>`; | |
| root.querySelector('#open').onclick = (e) => { | |
| api.open(); e.target.classList.add('hidden'); | |
| root.querySelector('#after').classList.remove('hidden'); | |
| api.log('質問Sparkを開封'); | |
| }; | |
| const answer = (text) => { | |
| api.act('answer'); api.loop(); | |
| api.toast('回答した(撮影なし)'); | |
| api.log('回答: ' + text + ' → 低コスト完了'); | |
| }; | |
| root.querySelectorAll('.chip').forEach(btn => btn.onclick = () => answer(btn.dataset.a)); | |
| root.querySelector('#send').onclick = () => answer(root.querySelector('#msg').value.trim() || '(自由記述)'); | |
| } | |
| } | |
| ]; | |
| const state = { open: 0, act: 0, loop: 0 }; | |
| const logEl = document.getElementById('log'); | |
| const toastEl = document.getElementById('toast'); | |
| let toastTimer; | |
| function apiFactory() { | |
| return { | |
| open() { state.open++; syncMetrics(); }, | |
| act() { state.act++; syncMetrics(); }, | |
| loop() { state.loop++; syncMetrics(); }, | |
| log(msg) { | |
| const t = new Date().toLocaleTimeString('ja-JP', { hour12: false }); | |
| const prev = logEl.textContent === '操作するとここに流れます' ? '' : logEl.textContent + '\n'; | |
| logEl.textContent = prev + `${t} ${msg}`; | |
| logEl.scrollTop = logEl.scrollHeight; | |
| }, | |
| toast(msg) { | |
| toastEl.textContent = msg; | |
| toastEl.classList.add('show'); | |
| clearTimeout(toastTimer); | |
| toastTimer = setTimeout(() => toastEl.classList.remove('show'), 2200); | |
| } | |
| }; | |
| } | |
| function syncMetrics() { | |
| document.getElementById('m-open').textContent = state.open; | |
| document.getElementById('m-act').textContent = state.act; | |
| document.getElementById('m-loop').textContent = state.loop; | |
| } | |
| function setDemo(id) { | |
| const demo = demos.find(d => d.id === id) || demos[0]; | |
| document.querySelectorAll('.tab').forEach(t => t.classList.toggle('active', t.dataset.id === demo.id)); | |
| document.getElementById('meta').innerHTML = ` | |
| <span class="tag ${demo.tagClass}">${demo.tag}</span> | |
| <h2>${demo.title}</h2> | |
| <div class="hyp">${demo.hyp}<br><b style="color:var(--text)">見る指標:</b> ${demo.measure}</div> | |
| <div class="steps"> | |
| <div class="step"><i>1</i><div>スマホ枠で一連の操作をする</div></div> | |
| <div class="step"><i>2</i><div>「行動した気になるか/返したくなるか」を体感で見る</div></div> | |
| <div class="step"><i>3</i><div>右のカウンタはデモ用。本番は開封→行動→還流で取る</div></div> | |
| </div>`; | |
| const screen = document.getElementById('screen'); | |
| screen.innerHTML = ''; | |
| demo.render(screen, apiFactory()); | |
| } | |
| const tabs = document.getElementById('tabs'); | |
| demos.forEach(d => { | |
| const b = document.createElement('button'); | |
| b.className = 'tab'; | |
| b.textContent = d.tab; | |
| b.dataset.id = d.id; | |
| b.onclick = () => setDemo(d.id); | |
| tabs.appendChild(b); | |
| }); | |
| setDemo('current'); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment