02리드 마그넷 다운로드 카드
이메일과 동의 칸을 채우면 잠금이 풀리며 진행 막대가 차고 파일 칩이 튀어나와 실제로 눌러 받을 수 있습니다. 받았다는 사실은 localStorage 에 남아 다음 방문엔 바로 칩이 떠 있습니다.
쓰임블로그 사이드바 무료 자료 · 가이드 PDF 받기 · 체크리스트 다운로드
localStoragescaleXaria-live

<main class="stage is-demo">
<form class="lm" id="lm" novalidate>
<p class="lm__title">시작 가이드 무료로 받기</p>
<p class="lm__file">
<span class="lm__file-ic" aria-hidden="true"><svg viewBox="0 0 256 256" width="16" height="16"><g fill="currentColor"><path d="M208 88h-56V32Z" opacity=".2"/><path d="M224 152a8 8 0 0 1-8 8h-24v16h16a8 8 0 0 1 0 16h-16v16a8 8 0 0 1-16 0v-56a8 8 0 0 1 8-8h32a8 8 0 0 1 8 8M92 172a28 28 0 0 1-28 28h-8v8a8 8 0 0 1-16 0v-56a8 8 0 0 1 8-8h16a28 28 0 0 1 28 28m-16 0a12 12 0 0 0-12-12h-8v24h8a12 12 0 0 0 12-12m88 8a36 36 0 0 1-36 36h-16a8 8 0 0 1-8-8v-56a8 8 0 0 1 8-8h16a36 36 0 0 1 36 36m-16 0a20 20 0 0 0-20-20h-8v40h8a20 20 0 0 0 20-20M40 112V40a16 16 0 0 1 16-16h96a8 8 0 0 1 5.66 2.34l56 56A8 8 0 0 1 216 88v24a8 8 0 0 1-16 0V96h-48a8 8 0 0 1-8-8V40H56v72a8 8 0 0 1-16 0m120-32h28.69L160 51.31Z"/></g></svg></span>
vibe-start-guide.pdf · 2.4MB
</p>
<label class="lm__lab" for="lm-email">이메일 주소</label>
<input class="lm__in" id="lm-email" type="email" placeholder="[email protected]" autocomplete="email">
<label class="lm__agree" for="lm-ok">
<input class="lm__cb" id="lm-ok" type="checkbox">
<span>업데이트 소식도 받을게요 (언제든 해지)</span>
</label>
<button class="lm__btn" id="lm-btn" type="submit">
<span class="lm__prog" aria-hidden="true"></span>
<span class="lm__btn-t">
<span class="lm__btn-ic" aria-hidden="true"><svg viewBox="0 0 256 256" width="16" height="16"><g fill="currentColor"><path d="M216 48v160H40V48a16 16 0 0 1 16-16h144a16 16 0 0 1 16 16" opacity=".2"/><path d="M224 144v64a8 8 0 0 1-8 8H40a8 8 0 0 1-8-8v-64a8 8 0 0 1 16 0v56h160v-56a8 8 0 0 1 16 0m-101.66 5.66a8 8 0 0 0 11.32 0l40-40a8 8 0 0 0-11.32-11.32L136 124.69V32a8 8 0 0 0-16 0v92.69L93.66 98.34a8 8 0 0 0-11.32 11.32Z"/></g></svg></span>
자료 받기
</span>
</button>
<p class="lm__msg" id="lm-msg" role="status" aria-live="polite">이메일만 넣으면 바로 받을 수 있어요</p>
<a class="lm__chip" id="lm-chip" href="data:text/plain,vibe-start-guide%20sample" download="vibe-start-guide.txt">
<span class="lm__chip-ic" aria-hidden="true"><svg viewBox="0 0 256 256" width="16" height="16"><g fill="currentColor"><path d="M216 96v112a8 8 0 0 1-8 8H48a8 8 0 0 1-8-8V96a8 8 0 0 1 8-8h160a8 8 0 0 1 8 8" opacity=".2"/><path d="M208 80H96V56a32 32 0 0 1 32-32c15.37 0 29.2 11 32.16 25.59a8 8 0 0 0 15.68-3.18C171.32 24.15 151.2 8 128 8a48.05 48.05 0 0 0-48 48v24H48a16 16 0 0 0-16 16v112a16 16 0 0 0 16 16h160a16 16 0 0 0 16-16V96a16 16 0 0 0-16-16m0 128H48V96h160z"/></g></svg></span>
다운로드 준비됨 — 눌러서 받기
</a>
</form>
</main>@use "../../../../site/static/demo-tokens" as *;
// ── 변수 3개 ──────────────────────────────────────────
$duration: $dur-loop;
$easing: $ease-pop;
$color: $subject-blue;
@include stage($stage-yellow);
.lm {
box-sizing: border-box; position: relative; width: min(320px, 94vw);
display: flex; flex-direction: column; gap: $sp-2;
background: $stage-paper; color: $subject-ink;
border-radius: $r-card; padding: $sp-3; box-shadow: $shadow-raised;
}
.lm__title { margin: 0; font-weight: 800; font-size: 14px; line-height: 1.3; }
.lm__file {
display: flex; align-items: center; gap: $sp-1; margin: 0;
padding: $sp-1 $sp-2; border-radius: $r-xs;
background: rgba($color, .1); color: $subject-blue-deep; // 연파랑 면 위 파란 글자 3.96:1 → deep 5.58:1
font-weight: 700; font-size: 11px; line-height: 1.2;
}
.lm__file-ic { display: inline-flex; }
.lm__lab { font-weight: 700; font-size: 10px; line-height: 1.2; color: rgba(23, 20, 26, .62); }
.lm__in {
padding: $sp-2 $sp-3;
border: 1.5px solid rgba(23, 20, 26, .16); border-radius: $r-sm;
font-weight: 500; font-size: 12px; line-height: 1.2; color: $subject-ink;
}
.lm__in:focus-visible { outline: 2px solid $color; outline-offset: 1px; }
.lm__in.is-shake { animation-name: lm-shake; animation-duration: $dur-base; animation-timing-function: $ease-out; }
@keyframes lm-shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-5px); }
75% { transform: translateX(5px); }
}
.lm__agree {
display: flex; align-items: center; gap: $sp-1;
font-weight: 500; font-size: 10px; line-height: 1.3; color: rgba(23, 20, 26, .72);
}
.lm__cb { width: 14px; height: 14px; margin: 0; accent-color: $color; }
.lm__btn {
position: relative; overflow: hidden;
padding: $sp-2 $sp-3; border: 0; border-radius: $r-control;
background: $color; color: $stage-paper;
font-weight: 800; font-size: 12px; line-height: 1.2; cursor: pointer;
}
.lm__btn-t { position: relative; display: inline-flex; align-items: center; justify-content: center; gap: $sp-1; }
.lm__btn-ic { display: inline-flex; }
.lm__btn[disabled] { cursor: progress; }
// 진행 막대 — 폭이 아니라 scaleX 라 레이아웃을 다시 계산하지 않는다
.lm__prog {
position: absolute; inset: 0; background: rgba(255, 255, 255, .34);
transform: scaleX(0); transform-origin: left center; // 규칙8: 좌변서 차오름
}
.lm.is-busy .lm__prog {
animation-name: lm-fill; animation-duration: 900ms;
animation-timing-function: $ease-out; animation-fill-mode: forwards;
}
@keyframes lm-fill {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
.lm__msg { margin: 0; min-height: 13px; font-weight: 500; font-size: 10px; line-height: 1.3; color: rgba(23, 20, 26, .6); }
.lm__chip {
display: flex; align-items: center; gap: $sp-1;
padding: $sp-1 $sp-2; border-radius: $r-pill;
background: rgba($subject-mint, .22); color: $subject-ink;
font-weight: 800; font-size: 11px; line-height: 1.3; text-decoration: none;
opacity: 0; transform: translateY(10px); pointer-events: none;
transition: opacity $dur-quick $ease-out, transform $dur-base $easing;
}
.lm__chip-ic { display: inline-flex; color: $subject-mint; }
.lm.is-done .lm__chip { opacity: 1; transform: translateY(0); pointer-events: auto; }
// ── 측정용 자동 루프 ────────────────────────────────
// 막대가 차고(scaleX) → 칩이 한 번 올라와 앉는다. 칩이 사라지는 장면은 재현하지 않는다(퇴장은 루프 밖 첫 프레임에만).
.stage.is-demo .lm__prog {
animation-name: lm-loop-fill; animation-duration: $duration;
animation-timing-function: $ease-out; animation-iteration-count: infinite;
}
@keyframes lm-loop-fill {
0% { transform: scaleX(0); }
38% { transform: scaleX(1); }
86% { transform: scaleX(1); }
86.01% { transform: scaleX(0); }
100% { transform: scaleX(0); }
}
.stage.is-demo .lm__chip {
animation-name: lm-loop-chip; animation-duration: $duration;
animation-timing-function: $easing; animation-iteration-count: infinite;
animation-fill-mode: backwards;
}
@keyframes lm-loop-chip {
0%, 40% { opacity: 0; transform: translateY(10px); }
40.01% { opacity: 1; transform: translateY(10px); }
60% { opacity: 1; transform: translateY(0); }
100% { opacity: 1; transform: translateY(0); }
}
// 루프 중에는 입력칸 안내글자를 숨긴다 — 겹쳐 읽히는 글자를 만들지 않는다
.stage.is-demo .lm__in::placeholder { color: transparent; }
// ── 접근성 ──────────────────────────────────────────
@media (prefers-reduced-motion: reduce) {
.lm__in.is-shake, .lm.is-busy .lm__prog, .stage.is-demo .lm__prog, .stage.is-demo .lm__chip, .lm__chip {
animation: none !important;
transition: none !important;
}
.stage.is-demo .lm__prog { transform: scaleX(1) !important; }
.stage.is-demo .lm__chip { opacity: 1 !important; transform: translateY(0) !important; }
}
// ── 반응형(320×200) ──────────────────────────────────
@media (max-width: 340px), (max-height: 220px) {
.lm { width: min(288px, 92vw); gap: $sp-1; padding: $sp-2; }
.lm__title { font-size: 12px; }
.lm__file { padding: 0 $sp-1; font-size: 9px; }
.lm__file-ic svg { width: 12px; height: 12px; }
.lm__lab { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; }
.lm__in { padding: $sp-1 $sp-2; font-size: 10px; }
.lm__agree { font-size: 9px; }
.lm__cb { width: 12px; height: 12px; }
.lm__btn { padding: $sp-1 $sp-2; font-size: 10px; }
.lm__btn-ic svg { width: 12px; height: 12px; }
.lm__msg { min-height: 0; font-size: 9px; }
.lm__chip { padding: 0 $sp-1; font-size: 9px; }
.lm__chip-ic svg { width: 12px; height: 12px; }
}html, body {
margin: 0;
overflow: hidden;
}
.stage {
box-sizing: border-box;
min-height: 100vh;
display: grid;
place-items: center;
margin: 0;
background: #ffd23f;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
padding: clamp(8px, 4vw, 24px);
}
.lm {
box-sizing: border-box;
position: relative;
width: min(320px, 94vw);
display: flex;
flex-direction: column;
gap: 8px;
background: #ffffff;
color: #17141a;
border-radius: 16px;
padding: 12px;
box-shadow: 0 14px 24px rgba(23, 20, 26, 0.32);
}
.lm__title {
margin: 0;
font-weight: 800;
font-size: 14px;
line-height: 1.3;
}
.lm__file {
display: flex;
align-items: center;
gap: 4px;
margin: 0;
padding: 4px 8px;
border-radius: 4px;
background: rgba(47, 109, 246, 0.1);
color: #0a4fe8;
font-weight: 700;
font-size: 11px;
line-height: 1.2;
}
.lm__file-ic {
display: inline-flex;
}
.lm__lab {
font-weight: 700;
font-size: 10px;
line-height: 1.2;
color: rgba(23, 20, 26, 0.62);
}
.lm__in {
padding: 8px 12px;
border: 1.5px solid rgba(23, 20, 26, 0.16);
border-radius: 8px;
font-weight: 500;
font-size: 12px;
line-height: 1.2;
color: #17141a;
}
.lm__in:focus-visible {
outline: 2px solid #2f6df6;
outline-offset: 1px;
}
.lm__in.is-shake {
animation-name: lm-shake;
animation-duration: 300ms;
animation-timing-function: ease-out;
}
@keyframes lm-shake {
0%, 100% {
transform: translateX(0);
}
25% {
transform: translateX(-5px);
}
75% {
transform: translateX(5px);
}
}
.lm__agree {
display: flex;
align-items: center;
gap: 4px;
font-weight: 500;
font-size: 10px;
line-height: 1.3;
color: rgba(23, 20, 26, 0.72);
}
.lm__cb {
width: 14px;
height: 14px;
margin: 0;
accent-color: #2f6df6;
}
.lm__btn {
position: relative;
overflow: hidden;
padding: 8px 12px;
border: 0;
border-radius: 12px;
background: #2f6df6;
color: #ffffff;
font-weight: 800;
font-size: 12px;
line-height: 1.2;
cursor: pointer;
}
.lm__btn-t {
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 4px;
}
.lm__btn-ic {
display: inline-flex;
}
.lm__btn[disabled] {
cursor: progress;
}
.lm__prog {
position: absolute;
inset: 0;
background: rgba(255, 255, 255, 0.34);
transform: scaleX(0);
transform-origin: left center;
}
.lm.is-busy .lm__prog {
animation-name: lm-fill;
animation-duration: 900ms;
animation-timing-function: ease-out;
animation-fill-mode: forwards;
}
@keyframes lm-fill {
from {
transform: scaleX(0);
}
to {
transform: scaleX(1);
}
}
.lm__msg {
margin: 0;
min-height: 13px;
font-weight: 500;
font-size: 10px;
line-height: 1.3;
color: rgba(23, 20, 26, 0.6);
}
.lm__chip {
display: flex;
align-items: center;
gap: 4px;
padding: 4px 8px;
border-radius: 999px;
background: rgba(76, 212, 166, 0.22);
color: #17141a;
font-weight: 800;
font-size: 11px;
line-height: 1.3;
text-decoration: none;
opacity: 0;
transform: translateY(10px);
pointer-events: none;
transition: opacity 220ms ease-out, transform 300ms cubic-bezier(0.2, 1.4, 0.4, 1);
}
.lm__chip-ic {
display: inline-flex;
color: #4cd4a6;
}
.lm.is-done .lm__chip {
opacity: 1;
transform: translateY(0);
pointer-events: auto;
}
.stage.is-demo .lm__prog {
animation-name: lm-loop-fill;
animation-duration: 2s;
animation-timing-function: ease-out;
animation-iteration-count: infinite;
}
@keyframes lm-loop-fill {
0% {
transform: scaleX(0);
}
38% {
transform: scaleX(1);
}
86% {
transform: scaleX(1);
}
86.01% {
transform: scaleX(0);
}
100% {
transform: scaleX(0);
}
}
.stage.is-demo .lm__chip {
animation-name: lm-loop-chip;
animation-duration: 2s;
animation-timing-function: cubic-bezier(0.2, 1.4, 0.4, 1);
animation-iteration-count: infinite;
animation-fill-mode: backwards;
}
@keyframes lm-loop-chip {
0%, 40% {
opacity: 0;
transform: translateY(10px);
}
40.01% {
opacity: 1;
transform: translateY(10px);
}
60% {
opacity: 1;
transform: translateY(0);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
.stage.is-demo .lm__in::placeholder {
color: transparent;
}
@media (prefers-reduced-motion: reduce) {
.lm__in.is-shake, .lm.is-busy .lm__prog, .stage.is-demo .lm__prog, .stage.is-demo .lm__chip, .lm__chip {
animation: none !important;
transition: none !important;
}
.stage.is-demo .lm__prog {
transform: scaleX(1) !important;
}
.stage.is-demo .lm__chip {
opacity: 1 !important;
transform: translateY(0) !important;
}
}
@media (max-width: 340px), (max-height: 220px) {
.lm {
width: min(288px, 92vw);
gap: 4px;
padding: 8px;
}
.lm__title {
font-size: 12px;
}
.lm__file {
padding: 0 4px;
font-size: 9px;
}
.lm__file-ic svg {
width: 12px;
height: 12px;
}
.lm__lab {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
}
.lm__in {
padding: 4px 8px;
font-size: 10px;
}
.lm__agree {
font-size: 9px;
}
.lm__cb {
width: 12px;
height: 12px;
}
.lm__btn {
padding: 4px 8px;
font-size: 10px;
}
.lm__btn-ic svg {
width: 12px;
height: 12px;
}
.lm__msg {
min-height: 0;
font-size: 9px;
}
.lm__chip {
padding: 0 4px;
font-size: 9px;
}
.lm__chip-ic svg {
width: 12px;
height: 12px;
}
}// 실제 동작: 이메일 정규식과 동의 칸을 함께 보고, 통과하면 버튼을 aria-busy 로 잠근 뒤 진행 막대를 900ms 채우고
// 다운로드 칩을 띄운다. 한 번 받은 사람은 localStorage 에 남겨 다음 방문에 곧장 완료 상태로 연다.
var stage = document.querySelector('.stage');
var form = document.getElementById('lm');
var input = document.getElementById('lm-email');
var agree = document.getElementById('lm-ok');
var btn = document.getElementById('lm-btn');
var msg = document.getElementById('lm-msg');
var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
function human() { stage.classList.remove('is-demo'); }
function finish() {
form.classList.add('is-done');
msg.textContent = '자료가 준비됐어요. 아래에서 받으세요';
}
try { if (localStorage.getItem('lead271')) finish(); } catch (e) { /* iframe 이 저장소를 막으면 처음 받는 사람과 똑같이 논다 */ }
form.addEventListener('submit', function (e) {
e.preventDefault();
human();
if (!EMAIL_RE.test(input.value) || !agree.checked) {
input.classList.remove('is-shake');
void input.offsetWidth;
input.classList.add('is-shake');
msg.textContent = '이메일과 동의 칸을 확인해 주세요';
return;
}
btn.disabled = true;
btn.setAttribute('aria-busy', 'true');
form.classList.add('is-busy');
msg.textContent = '파일을 준비하고 있어요';
setTimeout(function () {
form.classList.remove('is-busy');
btn.disabled = false;
btn.removeAttribute('aria-busy');
try { localStorage.setItem('lead271', '1'); } catch (e2) { /* 저장 실패해도 이번 화면은 완료로 간다 */ }
finish();
}, 900);
});
stage.addEventListener('pointerdown', human, { once: true });
window.addEventListener('keydown', human, { once: true });import { useEffect, useState } from "react";
import type { CSSProperties } from "react";
import styles from "./LeadMagnetDownload.module.scss";
export type LeadMagnetDownloadProps = {
/** 격자 미리보기용 루프 길이(ms) */
duration?: number;
easing?: string;
color?: string;
};
type Vars = CSSProperties & { "--duration"?: string; "--easing"?: string; "--color"?: string };
type FormEvt = { preventDefault(): void };
type InputEvt = { target: { value: string } };
type CheckEvt = { target: { checked: boolean } };
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const storageKey = "lead271";
function FilePdf() {
return (
<svg viewBox="0 0 256 256" width="16" height="16" aria-hidden="true">
<g fill="currentColor">
<path d="M208 88h-56V32Z" opacity=".2" />
<path d="M224 152a8 8 0 0 1-8 8h-24v16h16a8 8 0 0 1 0 16h-16v16a8 8 0 0 1-16 0v-56a8 8 0 0 1 8-8h32a8 8 0 0 1 8 8M92 172a28 28 0 0 1-28 28h-8v8a8 8 0 0 1-16 0v-56a8 8 0 0 1 8-8h16a28 28 0 0 1 28 28m-16 0a12 12 0 0 0-12-12h-8v24h8a12 12 0 0 0 12-12m88 8a36 36 0 0 1-36 36h-16a8 8 0 0 1-8-8v-56a8 8 0 0 1 8-8h16a36 36 0 0 1 36 36m-16 0a20 20 0 0 0-20-20h-8v40h8a20 20 0 0 0 20-20M40 112V40a16 16 0 0 1 16-16h96a8 8 0 0 1 5.66 2.34l56 56A8 8 0 0 1 216 88v24a8 8 0 0 1-16 0V96h-48a8 8 0 0 1-8-8V40H56v72a8 8 0 0 1-16 0m120-32h28.69L160 51.31Z" />
</g>
</svg>
);
}
function DownloadSimple() {
return (
<svg viewBox="0 0 256 256" width="16" height="16" aria-hidden="true">
<g fill="currentColor">
<path d="M216 48v160H40V48a16 16 0 0 1 16-16h144a16 16 0 0 1 16 16" opacity=".2" />
<path d="M224 144v64a8 8 0 0 1-8 8H40a8 8 0 0 1-8-8v-64a8 8 0 0 1 16 0v56h160v-56a8 8 0 0 1 16 0m-101.66 5.66a8 8 0 0 0 11.32 0l40-40a8 8 0 0 0-11.32-11.32L136 124.69V32a8 8 0 0 0-16 0v92.69L93.66 98.34a8 8 0 0 0-11.32 11.32Z" />
</g>
</svg>
);
}
function LockSimpleOpen() {
return (
<svg viewBox="0 0 256 256" width="16" height="16" aria-hidden="true">
<g fill="currentColor">
<path d="M216 96v112a8 8 0 0 1-8 8H48a8 8 0 0 1-8-8V96a8 8 0 0 1 8-8h160a8 8 0 0 1 8 8" opacity=".2" />
<path d="M208 80H96V56a32 32 0 0 1 32-32c15.37 0 29.2 11 32.16 25.59a8 8 0 0 0 15.68-3.18C171.32 24.15 151.2 8 128 8a48.05 48.05 0 0 0-48 48v24H48a16 16 0 0 0-16 16v112a16 16 0 0 0 16 16h160a16 16 0 0 0 16-16V96a16 16 0 0 0-16-16m0 128H48V96h160z" />
</g>
</svg>
);
}
export default function LeadMagnetDownload({
duration = 2000,
easing = "cubic-bezier(.2,1.4,.4,1)",
color = "#2f6df6",
}: LeadMagnetDownloadProps) {
const [email, setEmail] = useState("");
const [agreed, setAgreed] = useState(false);
const [busy, setBusy] = useState(false);
const [done, setDone] = useState(false);
const [shake, setShake] = useState(false);
const [msg, setMsg] = useState("이메일만 넣으면 바로 받을 수 있어요");
const [loop, setLoop] = useState(true);
// 한 번 받은 사람은 localStorage 에 남겨 다음 방문에 곧장 완료 상태로 연다
useEffect(() => {
try {
if (localStorage.getItem(storageKey)) {
setDone(true);
setMsg("자료가 준비됐어요. 아래에서 받으세요");
}
} catch {
// iframe 이 저장소를 막으면 처음 받는 사람과 똑같이 논다
}
}, []);
function onSubmit(e: FormEvt) {
e.preventDefault();
setLoop(false);
if (!emailPattern.test(email) || !agreed) {
setShake(false);
requestAnimationFrame(() => setShake(true));
setMsg("이메일과 동의 칸을 확인해 주세요");
return;
}
setBusy(true);
setMsg("파일을 준비하고 있어요");
setTimeout(() => {
setBusy(false);
try {
localStorage.setItem(storageKey, "1");
} catch {
// 저장 실패해도 이번 화면은 완료로 간다
}
setDone(true);
setMsg("자료가 준비됐어요. 아래에서 받으세요");
}, 900);
}
const vars: Vars = { "--duration": `${duration}ms`, "--easing": easing, "--color": color };
const cls = [styles.lm, loop ? styles["is-demo"] : "", busy ? styles["is-busy"] : "", done ? styles["is-done"] : ""]
.filter(Boolean)
.join(" ");
return (
<form className={cls} style={vars} onSubmit={onSubmit} onPointerDown={() => setLoop(false)} noValidate>
<p className={styles.lm__title}>시작 가이드 무료로 받기</p>
<p className={styles.lm__file}>
<span className={styles["lm__file-ic"]} aria-hidden="true"><FilePdf /></span>
vibe-start-guide.pdf · 2.4MB
</p>
<label className={styles.lm__lab} htmlFor="lm-email-r">이메일 주소</label>
<input
className={[styles.lm__in, shake ? styles["is-shake"] : ""].filter(Boolean).join(" ")}
id="lm-email-r"
type="email"
placeholder="[email protected]"
autoComplete="email"
value={email}
onChange={(e: InputEvt) => setEmail(e.target.value)}
onAnimationEnd={() => setShake(false)}
/>
<label className={styles.lm__agree} htmlFor="lm-ok-r">
<input
className={styles.lm__cb}
id="lm-ok-r"
type="checkbox"
checked={agreed}
onChange={(e: CheckEvt) => setAgreed(e.target.checked)}
/>
<span>업데이트 소식도 받을게요 (언제든 해지)</span>
</label>
<button className={styles.lm__btn} type="submit" disabled={busy} aria-busy={busy}>
<span className={styles.lm__prog} aria-hidden="true" />
<span className={styles["lm__btn-t"]}>
<span className={styles["lm__btn-ic"]} aria-hidden="true"><DownloadSimple /></span>
자료 받기
</span>
</button>
<p className={styles.lm__msg} role="status" aria-live="polite">{msg}</p>
<a
className={styles.lm__chip}
href="data:text/plain,vibe-start-guide%20sample"
download="vibe-start-guide.txt"
>
<span className={styles["lm__chip-ic"]} aria-hidden="true"><LockSimpleOpen /></span>
다운로드 준비됨 — 눌러서 받기
</a>
</form>
);
}02 리드 마그넷 다운로드 카드 — 측정 누적면적 13.067% · 움직인 프레임 15/23GODRICH 원작 · zip 에 SCSS·React 포함 · 랜딩페이지 전환율을 끌어올리는 섹션 9종
이 데모가 속한 글
랜딩페이지 전환율을 끌어올리는 섹션 9종랜딩페이지 전환율이란 방문자가 이메일을 남기거나 예약·구매까지 가는 비율을 말하고, 이 글은 그 전환을 직접 받아내는 섹션 아홉 개를 통째로 주는 코드 모음입니다. 떠나는 방문자를 붙잡는 이탈 팝업부터 읽던 지점에 이 글의 데모 전부 보기


