mirror of
https://github.com/nexu-io/open-design.git
synced 2026-06-01 03:14:35 +07:00
Introduce a Swiss editorial user-research live-artifact template with self-contained HTML seed/example and checklist, and register i18n fallback ids for DE/FR/RU so localized coverage stays green. Co-authored-by: Tuola Ge <gexingli@refly.ai> Co-authored-by: Cursor <cursoragent@cursor.com>
265 lines
9.8 KiB
HTML
265 lines
9.8 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>User Research Synthesis Example</title>
|
|
<style>
|
|
:root {
|
|
--paper: #f7f6ea;
|
|
--ink: #161616;
|
|
--muted: #5d5a51;
|
|
--line: rgba(22, 22, 22, 0.2);
|
|
--seg1: #171717;
|
|
--seg2: #595959;
|
|
--seg3: #8b8b84;
|
|
--seg4: #c8c8bf;
|
|
}
|
|
* { box-sizing: border-box; }
|
|
body {
|
|
margin: 0;
|
|
font-family: Inter, "Helvetica Neue", Arial, sans-serif;
|
|
background: var(--paper);
|
|
color: var(--ink);
|
|
-webkit-font-smoothing: antialiased;
|
|
min-height: 100vh;
|
|
}
|
|
.deck {
|
|
max-width: 1360px;
|
|
min-height: 860px;
|
|
margin: 26px auto;
|
|
border: 1px solid rgba(22, 22, 22, 0.08);
|
|
background: var(--paper);
|
|
box-shadow: 0 18px 60px rgba(0, 0, 0, 0.08);
|
|
display: flex;
|
|
flex-direction: column;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
.toolbar {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 14px 20px;
|
|
border-bottom: 1px solid var(--line);
|
|
font-size: 12px;
|
|
letter-spacing: 0.18em;
|
|
text-transform: uppercase;
|
|
color: var(--muted);
|
|
}
|
|
.toolbar .btns { display: flex; gap: 8px; }
|
|
.toolbar button {
|
|
border: 1px solid var(--line);
|
|
background: transparent;
|
|
color: var(--ink);
|
|
border-radius: 999px;
|
|
height: 30px;
|
|
padding: 0 12px;
|
|
font-size: 11px;
|
|
letter-spacing: 0.08em;
|
|
cursor: pointer;
|
|
}
|
|
.slides { position: relative; flex: 1; }
|
|
.slide {
|
|
position: absolute;
|
|
inset: 0;
|
|
padding: 52px 64px;
|
|
opacity: 0;
|
|
transform: translateX(28px);
|
|
transition: opacity .45s ease, transform .45s ease;
|
|
pointer-events: none;
|
|
}
|
|
.slide.active {
|
|
opacity: 1;
|
|
transform: translateX(0);
|
|
pointer-events: auto;
|
|
}
|
|
.rule-top, .rule-bottom {
|
|
height: 1px;
|
|
background: var(--line);
|
|
transform-origin: 0 50%;
|
|
transform: scaleX(0);
|
|
transition: transform .6s ease;
|
|
}
|
|
.slide.active .rule-top, .slide.active .rule-bottom { transform: scaleX(1); }
|
|
.meta { font-size: 11px; letter-spacing: .24em; text-transform: uppercase; color: var(--muted); }
|
|
h1, h2 { font-weight: 300; letter-spacing: -0.02em; margin: 0; }
|
|
h1 { font-size: 88px; line-height: 1.02; margin-top: 24px; }
|
|
h2 { font-size: 58px; line-height: 1.05; margin-top: 18px; }
|
|
p { color: var(--muted); line-height: 1.55; }
|
|
.s1-main { margin-top: 84px; max-width: 980px; }
|
|
.s1-main p { font-size: 24px; max-width: 760px; margin-top: 26px; }
|
|
.foot {
|
|
position: absolute;
|
|
left: 64px;
|
|
right: 64px;
|
|
bottom: 44px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
font-size: 11px;
|
|
letter-spacing: .2em;
|
|
text-transform: uppercase;
|
|
color: var(--muted);
|
|
}
|
|
.s2-grid {
|
|
margin-top: 52px;
|
|
display: grid;
|
|
grid-template-columns: 420px 1fr;
|
|
gap: 80px;
|
|
align-items: center;
|
|
}
|
|
.donut-wrap { width: 420px; height: 420px; margin: 0 auto; }
|
|
.legend { display: grid; gap: 18px; }
|
|
.legend-row {
|
|
display: grid;
|
|
grid-template-columns: 16px 1fr auto;
|
|
gap: 14px;
|
|
align-items: center;
|
|
font-size: 21px;
|
|
transition: transform .2s ease;
|
|
}
|
|
.legend-row:hover { transform: translateX(6px); }
|
|
.swatch { width: 16px; height: 16px; border-radius: 2px; }
|
|
.pct { font-variant-numeric: tabular-nums; color: var(--muted); font-size: 20px; }
|
|
.s3-grid {
|
|
margin-top: 44px;
|
|
display: grid;
|
|
grid-template-columns: 1fr 520px;
|
|
gap: 52px;
|
|
}
|
|
.s3-grid p { font-size: 29px; max-width: 760px; margin-top: 16px; }
|
|
.bullets { margin-top: 30px; display: grid; gap: 14px; font-size: 20px; }
|
|
.bullets div { padding-left: 24px; position: relative; }
|
|
.bullets div::before { content: "—"; position: absolute; left: 0; color: var(--muted); }
|
|
.evidence {
|
|
border: 1px solid var(--line);
|
|
min-height: 360px;
|
|
display: grid;
|
|
place-items: center;
|
|
color: var(--muted);
|
|
position: relative;
|
|
background: linear-gradient(140deg, rgba(0, 0, 0, 0.02), rgba(0, 0, 0, 0.06));
|
|
transition: box-shadow .3s ease, transform .3s ease;
|
|
}
|
|
.evidence:hover {
|
|
box-shadow: 0 24px 48px rgba(0, 0, 0, 0.12);
|
|
transform: translateY(-2px);
|
|
}
|
|
.cap { font-size: 12px; letter-spacing: .1em; margin-top: 14px; color: var(--muted); }
|
|
.dots {
|
|
position: absolute;
|
|
left: 50%;
|
|
bottom: 14px;
|
|
transform: translateX(-50%);
|
|
display: flex;
|
|
gap: 12px;
|
|
z-index: 20;
|
|
}
|
|
.dots button {
|
|
width: 9px; height: 9px; border-radius: 50%;
|
|
border: 1px solid var(--ink);
|
|
background: transparent; cursor: pointer;
|
|
}
|
|
.dots button.active { background: var(--ink); transform: scale(1.3); }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<main class="deck">
|
|
<header class="toolbar">
|
|
<span>Swiss research synthesis</span>
|
|
<div class="btns">
|
|
<button type="button" id="prevBtn">Prev</button>
|
|
<button type="button" id="nextBtn">Next</button>
|
|
</div>
|
|
</header>
|
|
<section class="slides">
|
|
<article class="slide active" data-slide="0">
|
|
<div class="rule-top"></div>
|
|
<div class="s1-main">
|
|
<div class="meta">User research synthesis · May, 2026</div>
|
|
<h1>User Research Synthesis</h1>
|
|
<p>What we learned from 24 interviews and what it means for the product roadmap.</p>
|
|
</div>
|
|
<div class="rule-bottom" style="margin-top: 84px;"></div>
|
|
<div class="foot"><span>Research team · May, 2026</span><span>Round 1 · Internal</span></div>
|
|
</article>
|
|
|
|
<article class="slide" data-slide="1">
|
|
<div class="rule-top"></div>
|
|
<div class="meta" style="margin-top: 10px;">Participant breakdown</div>
|
|
<h2>Who we spoke with</h2>
|
|
<div class="s2-grid">
|
|
<div class="donut-wrap">
|
|
<svg viewBox="0 0 400 400" width="420" height="420" aria-label="Participant donut">
|
|
<g transform="translate(200 200) rotate(-90)" fill="none" stroke-width="38">
|
|
<circle r="118" cx="0" cy="0" stroke="var(--seg1)" stroke-dasharray="283.39 741.78"></circle>
|
|
<circle r="118" cx="0" cy="0" stroke="var(--seg2)" stroke-dasharray="186.44 741.78" stroke-dashoffset="-283.39"></circle>
|
|
<circle r="118" cx="0" cy="0" stroke="var(--seg3)" stroke-dasharray="163.19 741.78" stroke-dashoffset="-469.83"></circle>
|
|
<circle r="118" cx="0" cy="0" stroke="var(--seg4)" stroke-dasharray="108.76 741.78" stroke-dashoffset="-633.02"></circle>
|
|
</g>
|
|
</svg>
|
|
</div>
|
|
<div class="legend">
|
|
<div class="legend-row"><span class="swatch" style="background:var(--seg1)"></span><span>Power users</span><span class="pct">38%</span></div>
|
|
<div class="legend-row"><span class="swatch" style="background:var(--seg2)"></span><span>Casual users</span><span class="pct">25%</span></div>
|
|
<div class="legend-row"><span class="swatch" style="background:var(--seg3)"></span><span>Churn-risk users</span><span class="pct">22%</span></div>
|
|
<div class="legend-row"><span class="swatch" style="background:var(--seg4)"></span><span>Prospects</span><span class="pct">15%</span></div>
|
|
</div>
|
|
</div>
|
|
<div class="rule-bottom" style="margin-top: 18px;"></div>
|
|
<div class="foot"><span>Source: Recruitment screener</span><span>Total participants: 24</span></div>
|
|
</article>
|
|
|
|
<article class="slide" data-slide="2">
|
|
<div class="rule-top"></div>
|
|
<div class="s3-grid">
|
|
<div>
|
|
<div class="meta">The pattern</div>
|
|
<h2>The first 48 hours determine everything</h2>
|
|
<p>Users who complete three core actions in their first two days have a 4x higher 90-day retention rate.</p>
|
|
<div class="bullets">
|
|
<div>Onboarding drop-off peaks at step 3</div>
|
|
<div>"What do I do next?" is the most common exit trigger</div>
|
|
<div>Users who invite a teammate retain at 2x the rate</div>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<div class="evidence">Session image placeholder</div>
|
|
<div class="cap">Session recording review · April, 2026</div>
|
|
</div>
|
|
</div>
|
|
<div class="rule-bottom" style="margin-top: 24px;"></div>
|
|
<div class="foot"><span>User research synthesis</span><span>Research team</span></div>
|
|
</article>
|
|
</section>
|
|
<nav class="dots" aria-label="Slide navigation">
|
|
<button type="button" class="active" data-dot="0" aria-label="Slide 1"></button>
|
|
<button type="button" data-dot="1" aria-label="Slide 2"></button>
|
|
<button type="button" data-dot="2" aria-label="Slide 3"></button>
|
|
</nav>
|
|
</main>
|
|
<script>
|
|
(function () {
|
|
const slides = Array.from(document.querySelectorAll(".slide"));
|
|
const dots = Array.from(document.querySelectorAll(".dots button"));
|
|
let idx = 0;
|
|
const max = slides.length - 1;
|
|
|
|
function show(next) {
|
|
idx = Math.max(0, Math.min(max, next));
|
|
slides.forEach((el, i) => el.classList.toggle("active", i === idx));
|
|
dots.forEach((el, i) => el.classList.toggle("active", i === idx));
|
|
}
|
|
|
|
document.getElementById("prevBtn").addEventListener("click", () => show(idx - 1));
|
|
document.getElementById("nextBtn").addEventListener("click", () => show(idx + 1));
|
|
dots.forEach((dot, i) => dot.addEventListener("click", () => show(i)));
|
|
|
|
document.addEventListener("keydown", (event) => {
|
|
if (event.key === "ArrowLeft") show(idx - 1);
|
|
if (event.key === "ArrowRight") show(idx + 1);
|
|
});
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|