در درس قبل استوریبورد اسکرول را ساختیم؛ نقشهای که میگوید صفحه از چند پرده تشکیل شده و هر پرده چه میگوید. حالا وقت آن است که این نقشه را به ساختار واقعی HTML و CSS ترجمه کنیم.
این درس شاید کمهیجانترین قسمت این سری بهنظر برسد، اما در عمل تعیینکنندهترین است. نود درصد مشکلاتی که بعداً با انیمیشن اسکرول پیدا میکنی — پرش تصویر، پینشدن اشتباه، بههمریختن موبایل — ریشه در ساختار غلط HTML دارد، نه در کد انیمیشن. اگر اسکلت درست باشد، انیمیشن تقریباً خودش کار میکند.

لیست مطالب
قانون طلایی: صفحه بدون جاوااسکریپت هم باید کامل باشد
قبل از هر چیز این اصل را در ذهنت حک کن: اگر جاوااسکریپت را خاموش کنی، صفحه باید همچنان خوانا و کامل باشد. یعنی همهٔ متنها دیده شوند، همهٔ تصاویر سرِ جایشان باشند و کاربر بتواند کل محتوا را بخواند.
چرا اینقدر مهم است؟ سه دلیل:
- موتور جستجو: محتوایی که فقط با انیمیشن ظاهر میشود، ممکن است درست ایندکس نشود.
- خطا: اگر فایل انیمیشن لود نشود، بهجای صفحهٔ سفید، یک صفحهٔ ساده اما سالم میبینی.
- دسترسپذیری: کاربری که کاهش حرکت را فعال کرده، باید همان محتوا را ببیند.
ترجمهٔ عملی این اصل: انیمیشنها لایهای روی محتوا هستند، نه شرطِ دیدهشدنِ محتوا. عنصر را با opacity:0 در HTML پنهان نکن؛ اجازه بده جاوااسکریپت خودش قبل از شروع انیمیشن آن را پنهان کند.
اسکلت کلی صفحه
ساختار پایهای که برای تقریباً همهٔ صفحههای سینمایی جواب میدهد، این است:
<body>
<div id="smooth-wrapper"> <!-- ظرف اسکرول نرم -->
<div id="smooth-content">
<section class="scene scene--hero">...</section>
<section class="scene scene--stats">...</section>
<section class="scene scene--problem">...</section>
<section class="scene scene--pin">...</section>
<section class="scene scene--proof">...</section>
<section class="scene scene--cta">...</section>
</div>
</div>
</body>
سه نکتهٔ مهم در همین چند خط هست:
- هر پرده یک
sectionاست. نهdivتودرتو، نه ساختار عجیب. یک پرده = یک سکشن. - کلاس دوتایی: کلاس مشترک
sceneبرای رفتار عمومی، و کلاس اختصاصیscene--heroبرای تفاوتها. این الگو کدنویسی را تمیز نگه میدارد. - ظرف بیرونی برای اسکرول نرم: اگر بعداً بخواهی اسکرول نرم اضافه کنی، این دو لایه لازم میشوند. از همین ابتدا بگذارشان تا بعداً مجبور به بازنویسی نشوی.
چرا تودرتویی زیاد سم است
هر لایهٔ اضافه، یک overflow یا transform ناخواسته اضافه میکند که رفتار پین را خراب میکند. قاعدهٔ عملی: بین body و هر section، حداکثر دو لایه فاصله باشد.
آناتومی هیرو: اولین سه ثانیه
هیرو، اولین پرده و مهمترین بخش صفحه است. ساختار استانداردش:
<section class="scene scene--hero">
<div class="hero__media"> <!-- تصویر یا ویدئوی پسزمینه -->
<img src="hero.jpg" alt="...">
</div>
<div class="hero__veil"></div> <!-- لایهٔ تیره برای خوانایی متن -->
<div class="hero__content">
<h1 class="hero__title">یک جملهٔ کوتاه و قاطع</h1>
<p class="hero__sub">یک خط توضیح</p>
<a class="hero__cta" href="#contact">شروع کنید</a>
</div>
<div class="hero__hint" aria-hidden="true">اسکرول کنید</div>
</section>
و CSS پایهاش:
.scene--hero{
position:relative;
min-height:100svh; /* svh بهتر از vh روی موبایل */
display:grid;
place-items:center;
overflow:hidden;
}
.hero__media{position:absolute; inset:0;}
.hero__media img{width:100%; height:100%; object-fit:cover;}
.hero__veil{
position:absolute; inset:0;
background:linear-gradient(180deg,rgba(0,0,0,.25),rgba(0,0,0,.75));
}
.hero__content{position:relative; z-index:2; text-align:center; padding:0 6vw;}
.hero__title{font-size:clamp(2rem, 6vw, 5rem); line-height:1.25;}
دو نکته که تازهکارها معمولاً نمیدانند:
- از
100svhاستفاده کن، نه100vh. روی موبایل، نوار آدرس مرورگر هنگام اسکرول جمع میشود وvhباعث پرش ارتفاع میشود. واحدsvhاین مشکل را ندارد. - لایهٔ تیره (veil) را جدا نگه دار. اگر گرادیان را مستقیم روی خود تصویر بگذاری، بعداً نمیتوانی شدتش را با اسکرول تغییر دهی.

آناتومی بخش پینشده: قلب صفحهٔ سینمایی
بخش پینشده جایی است که صفحه ظاهراً میایستد و محتوای داخلش عوض میشود. این بخش بیشترین اشتباه ساختاری را به خود میبیند، پس دقیق نگاه کن:
<section class="scene scene--pin"> <!-- ظرف بلند: مسیر اسکرول -->
<div class="pin__stage"> <!-- چیزی که پین میشود -->
<h2 class="pin__title">چطور کار میکنیم</h2>
<div class="pin__panels">
<article class="panel" data-step="1">...</article>
<article class="panel" data-step="2">...</article>
<article class="panel" data-step="3">...</article>
</div>
</div>
</section>
منطق این ساختار را خوب بفهم، چون کلید همهچیز است:
| عنصر | نقش | ارتفاع |
|---|---|---|
.scene--pin |
مسیر اسکرول را میسازد | بلند، مثلاً ۳۰۰٪ صفحه |
.pin__stage |
چیزی که ثابت میماند | دقیقاً یک صفحه |
.panel |
محتوایی که عوض میشود | داخل استیج |
یعنی ظرف بیرونی بلند است تا اسکرول طول بکشد، و ظرف داخلی بهاندازهٔ یک صفحه است تا پینشدنش طبیعی باشد. اگر این دو را یکی کنی، یا پین کار نمیکند یا پرش وحشتناکی میبینی.
.scene--pin{height:300vh;} /* هر پنل ۱۰۰vh مسیر میخواهد */
.pin__stage{
height:100svh;
display:grid; place-items:center;
/* position:sticky یا پین با کتابخانه — در درس بعدی */
}
قاعدهٔ محاسبهٔ ارتفاع
فرمول ساده: ارتفاع ظرف = تعداد پنل × ۱۰۰vh، بهعلاوهٔ حدود ۵۰vh اضافه اگر میخواهی پنل آخر کمی روی صفحه بماند. سه پنل یعنی 300vh، چهار پنل یعنی 400vh. اگر ظرف کوتاه باشد، پنلها سریع رد میشوند و کاربر چیزی نمیفهمد.
آناتومی پردههای ساده
همهٔ پردهها پیچیده نیستند. پردههای «مسئله»، «تمایز» و «گواه» معمولاً فقط ظهور پلکانی دارند و ساختارشان بسیار ساده است:
<section class="scene scene--problem">
<div class="wrap">
<h2 data-reveal>بار گمشده یعنی مشتری ازدسترفته</h2>
<p data-reveal>توضیح کوتاه...</p>
<ul class="cards">
<li class="card" data-reveal>...</li>
<li class="card" data-reveal>...</li>
</ul>
</div>
</section>
اینجا data-reveal یک «نشانه» است: به جاوااسکریپت میگوید این عنصر باید هنگام ورود به کادر دید ظاهر شود. مزیت این روش نسبت به کلاسگذاری مستقیم، این است که با یک حلقهٔ ساده میتوانی همهٔ آنها را یکجا مدیریت کنی:
document.querySelectorAll('[data-reveal]').forEach(el => {
// انیمیشن ورود را اینجا ببند
});
این الگوی «صفت بهجای کلاس» را در تمام پروژه نگه دار؛ کدت را ده برابر تمیزتر میکند.
سیستم فاصله و ریتم عمودی
یکی از تفاوتهای نامرئی بین سایت آماتور و حرفهای، ریتم فاصلهها است. بهجای اینکه برای هر بخش عدد دلخواه بگذاری، یک مقیاس تعریف کن:
:root{
--space-1: .5rem; --space-2: 1rem;
--space-3: 2rem; --space-4: 4rem;
--space-5: 8rem; --space-6: 12rem;
--wrap: min(1200px, 90vw);
}
.wrap{width:var(--wrap); margin-inline:auto;}
.scene{padding-block:var(--space-5);}
.scene h2{margin-bottom:var(--space-3);}
فضای خالی زیاد بین پردهها، همان چیزی است که به صفحه حس «سینمایی» میدهد. اگر پردهها به هم چسبیده باشند، هرچقدر هم انیمیشن قشنگ باشد، صفحه شلوغ بهنظر میرسد. قاعدهٔ من: فاصلهٔ عمودی بین پردهها حداقل دو برابر چیزی باشد که فکر میکنی کافی است.

آمادهسازی برای انیمیشن: سه تنظیم ضروری
قبل از اینکه سراغ کتابخانهٔ انیمیشن بروی، این سه تنظیم را در CSS بگذار:
/* ۱) جلوگیری از اسکرول افقی ناخواسته */
html,body{overflow-x:hidden;}
/* ۲) احترام به تنظیم کاهش حرکت کاربر */
@media (prefers-reduced-motion: reduce){
*,*::before,*::after{
animation-duration:.01ms !important;
transition-duration:.01ms !important;
scroll-behavior:auto !important;
}
}
/* ۳) آمادهسازی لایه برای عناصری که قرار است حرکت کنند */
.will-move{will-change:transform, opacity;}
مورد دوم اختیاری نیست. بعضی کاربران با انیمیشن دچار سرگیجه یا حالت تهوع میشوند و مرورگرشان این تنظیم را اعلام میکند. نادیدهگرفتنش هم مشکل اخلاقی است و هم در ارزیابیهای کیفیت سایت امتیاز منفی میگیرد. در درس آخر این نقشهٔ راه بهطور کامل به این موضوع میپردازیم.
دربارهٔ مورد سوم هم محتاط باش: will-change را فقط روی عناصری بگذار که واقعاً حرکت میکنند. اگر روی همهچیز بگذاری، حافظهٔ کارت گرافیک پر میشود و نتیجه برعکس میشود.
چکلیست ساختار، قبل از نوشتن انیمیشن
| بررسی | چرا مهم است |
|---|---|
| با جاوااسکریپت خاموش، همهٔ متنها دیده میشوند؟ | سئو و پایداری |
هر پرده یک section با کلاس مشخص است؟ |
هدفگیری ساده در کد |
| ظرف پین بلند و استیج یکصفحهای است؟ | جلوگیری از پرش |
از svh بهجای vh استفاده شده؟ |
ثبات ارتفاع روی موبایل |
هیچ overflow:hidden اضافی روی والدها نیست؟ |
پین بدون دردسر |
تصاویر width و height دارند؟ |
جلوگیری از جابهجایی چیدمان |
بلوک prefers-reduced-motion اضافه شده؟ |
دسترسپذیری |
ساختن اسکلت با کمک هوش مصنوعی
حالا که میدانی ساختار درست چیست، میتوانی از مدل بخواهی آن را بسازد — اما با قید و بند دقیق:
نقش: توسعهدهندهٔ فرانتاند با تخصص در ساختار صفحات روایتمحور.
کار: فقط اسکلت HTML و CSS یک لندینگ سینمایی را بنویس.
هیچ جاوااسکریپتی ننویس.
پردهها به ترتیب: هیرو، آمار، مسئله، بخش پینشده با ۳ پنل،
گواه مشتری، دعوت به اقدام.
قیدها:
- هر پرده یک section با کلاس scene و یک کلاس اختصاصی.
- ظرف پین بلند (۳۰۰vh) و استیج داخلی ۱۰۰svh.
- از svh استفاده کن نه vh.
- عناصر متحرک فقط با صفت data-reveal علامت بخورند،
ولی در CSS پنهان نشوند.
- متغیرهای فاصله در :root تعریف شوند.
- بلوک prefers-reduced-motion را اضافه کن.
- بدون هیچ کتابخانهٔ خارجی.
خروجی: یک فایل HTML کامل با CSS داخلی + توضیح دوخطی
دربارهٔ منطق ارتفاع بخش پین.
تفاوت این پرامپت با «یک لندینگ سینمایی بساز» زمین تا آسمان است. اینجا تو معمار هستی و مدل فقط بنّا. همان منطقی که در آناتومی پرامپت حرفهای توضیح دادهایم: قید بگذار، خروجی را تعریف کن، و آنچه را نمیخواهی صریح ممنوع کن.
پرسشهای پرتکرار
آیا باید از فریمورک CSS استفاده کنم؟
برای این نوع صفحهها معمولاً نه. صفحههای سینمایی چیدمان اختصاصی دارند و فریمورک بیشتر سر راهت قرار میگیرد. چند متغیر CSS و چند کلاس ساده کافی است.
چرا نباید عناصر را در CSS با opacity:0 پنهان کنم؟
چون اگر جاوااسکریپت خطا بدهد یا لود نشود، آن محتوا برای همیشه نامرئی میماند. روش امن این است که جاوااسکریپت در همان ابتدای اجرا عناصر را پنهان کند؛ آنوقت اگر اجرا نشد، محتوا سالم باقی میماند.
بخش پینشده را با position:sticky بسازم یا با کتابخانه؟
اگر پین ساده است و فقط میخواهی چیزی بچسبد، sticky کافی و بسیار سبک است. اگر میخواهی انیمیشن داخل پین با اسکرول اسکراب شود، به کتابخانه نیاز داری — که موضوع دو درس بعدی است.
چند پرده برای یک لندینگ خوب کافی است؟
پنج تا هفت پرده. کمتر از آن معمولاً پیام کامل منتقل نمیشود و بیشتر از آن کاربر قبل از رسیدن به دکمهٔ اقدام خسته میشود.
جمعبندی
ساختار درست، نیمی از کار است. هر پرده یک section، ظرف پین بلند با استیج یکصفحهای، واحد svh بهجای vh، علامتگذاری با صفت بهجای پنهانکردن در CSS، سیستم فاصلهٔ منظم، و احترام به کاهش حرکت. اگر اینها را رعایت کنی، درسهای بعدی که وارد انیمیشن میشویم، تقریباً بدون دردسر پیش میروند.
در درس بعدی سراغ اسکرول نرم با Lenis میرویم؛ همان چیزی که به صفحه حس لغزندگی و سنگینیِ سینمایی میدهد. مسیر کامل را در نقشهٔ راه طراحی سایت سینمایی ببین و برای اطلاع از انتشار درسهای بعدی، کانال @MrChatGPT_IR را دنبال کن.
