تاروپود — خودآموز HTML و CSS

فصل ۷ از ۱۱

پیشرفت ترم
۰٪

ترم ۶ · حرفه‌ای و واقعی

سرعت

فصل ۷پیش‌نمایش رایگان
۱۰ دقیقه مطالعه فصل ۷

در این فصل چه می‌سازی#

یک صفحه می‌تواند درست ساخته شده باشد و باز هم بد باشد — اگر کند باز شود، یا موقعِ باز شدن جلوِ چشمِ کاربر بپرد و تکان بخورد. در این فصل چند عادتِ حرفه‌ای یاد می‌گیری که یک صفحهٔ سنگین و پرشی را به یک صفحهٔ سبک و آرام تبدیل می‌کنند. آخرِ فصل یک صفحهٔ سفرنامه داری که موقعِ بارگذاری هیچ تکانی نمی‌خورد: جای تصویر از قبل رزرو شده، تصویرش سبک است، و CSSِ بی‌استفاده‌اش را حذف کرده‌ای.

مقایسهٔ دو صفحه؛ سمتِ راست موقعِ بارگذاری متن‌ها با یک فلشِ پرش به پایین جهیده‌اند چون جای تصویر خالی بوده، سمتِ چپ همان صفحه که یک قابِ خالیِ رزروشده به‌جای تصویر دارد و متن‌ها سرِ جای نهایی‌شان آرام نشسته‌اند

آخر این فصل می‌توانی:

  • بگویی CLS چیست و چرا width/height روی تصویر جلویش را می‌گیرد
  • تصویرها را به‌اندازه و سبک نگه داری
  • با font-display جلوی متنِ نامرئیِ موقعِ بارگذاریِ فونت را بگیری
  • CSSِ بی‌استفاده را پیدا و حذف کنی

قبل از شروع#

  • از ترم ۱ فصل ۴ و ترم ۴ فصل ۳: <img> و width/height و تصویرِ responsive — همان بحث را اینجا به سرعت وصل می‌کنیم.
  • از ترم ۲: @font-face و فونتِ فارسی — این‌بار به بارگذاریِ فونت نگاه می‌کنیم.

مثل همیشه فقط یک مرورگر لازم داری، یا فقط همین آزمایشگاه.

۱. سرعت یک ویژگیِ طراحی است#

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

سرعت و پایداری چیزی نیست که آخرِ کار به صفحه اضافه کنی؛ یک ویژگیِ طراحی است، مثلِ رنگ و چیدمان. خبرِ خوب این است که چند عادتِ ساده بیشترِ مشکل را حل می‌کنند، و در این فصل همان‌ها را می‌بینیم.

۲. CLS: چرا صفحه می‌پرد#

آن پرشِ ناگهانی یک اسم دارد: CLS، یعنی جابه‌جاییِ ناگهانیِ چیدمان. شایع‌ترین علتش تصویری است که width و height ندارد. چرا؟ چون مرورگر تا وقتی تصویر نرسیده نمی‌داند چقدر جا لازم دارد، پس صفر جا می‌گذارد و متن را می‌چسباند بالا. بعد که تصویر رسید، ناگهان جای واقعی‌اش را باز می‌کند و همه‌چیزِ زیرش را هُل می‌دهد پایین.

بیا ببینیمش. تصویرِ صفحه را بدونِ اندازه بگذار — یعنی width و height را از تگش بردار:

<img class="hero" src="https://picsum.photos/id/1015/800/450" alt="رودخانه‌ای در جنگل">

حالا در DevTools مرورگر، بخشِ Network، سرعت را روی Slow 3G بگذار و صفحه را دوباره بار کن. نگاه کن: اول متن می‌چسبد بالا، بعد که تصویر می‌رسد، همه‌چیز یک تکانِ زشت می‌خورد و می‌پرد پایین. این همان CLS است.

🔧 اگر کار نکرد: مرورگر جای تصویر را نمی‌دانست چون هیچ اندازه‌ای ندادی. راهِ حل این است که همان width و heightِ اصلِ تصویر را روی تگ بنویسی. مرورگر از روی این دو، نسبتِ تصویر را می‌فهمد و از همان اول یک جای خالیِ درست‌اندازه رزرو می‌کند — پس وقتی تصویر می‌رسد، دقیقاً توی جای آماده می‌نشیند و چیزی تکان نمی‌خورد:

<img class="hero" src="https://picsum.photos/id/1015/800/450" alt="رودخانه‌ای در جنگل"
  width="800" height="450">

چک کن: دوباره با Slow 3G بار کن. این‌بار از همان اول یک جای خالیِ به‌اندازهٔ تصویر آنجاست، متن سرِ جای نهایی‌اش نشسته، و وقتی تصویر می‌رسد هیچ‌چیز نمی‌پرد. در CSS هم width: 100% و height: auto داریم تا تصویر روی هر عرضی جمع‌وجور شود، ولی نسبتش از همان دو عددِ تگ می‌آید.

💡 نکته: اگر اندازهٔ دقیقِ پیکسلیِ تصویر را نداری، به‌جای width/height می‌توانی در CSS از aspect-ratio استفاده کنی (مثلاً aspect-ratio: 16 / 9). آن هم جا را از قبل رزرو می‌کند و جلوی همان پرش را می‌گیرد.

قبل از ادامه، جواب بده: اگر یک <video> را هم بدونِ width/height بگذاری، همان پرش را می‌بینی؟

بله. CLS مخصوصِ تصویر نیست؛ هر چیزی که دیر بار می‌شود و جایش از قبل رزرو نشده — یک ویدیو، یک قابِ جاسازی‌شده — همان تکان را می‌سازد. برای همین عادتِ «جا را از قبل رزرو کن» یک قانونِ کلی است، نه فقط یک ترفندِ عکس.

۳. تصویر: اندازه و فرمت#

یک اشتباهِ رایجِ دیگر: یک تصویرِ غول‌پیکرِ ۳۰۰۰ پیکسلی را در یک جای ۴۰۰ پیکسلیِ صفحه می‌گذاری. مرورگر کلِ آن فایلِ سنگین را دانلود می‌کند، بعد کوچکش می‌کند تا جا شود — یعنی کاربر مِگابایت‌ها داده را برای چیزی که کوچک دیده می‌شود دانلود کرده. راهِ درست این است که تصویر را قبل از گذاشتن روی سایت به اندازهٔ واقعیِ نمایشش کوچک کنی.

یک تصویرِ خیلی بزرگ در سمتِ راست که با یک فلش فشرده می‌شود تا داخلِ یک قابِ کوچکِ سمتِ چپ جا شود؛ اختلافِ اندازهٔ آن دو نشان می‌دهد چه حجمِ اضافه‌ای بی‌جهت دانلود شده

فرمت هم مهم است. فرمت‌های امروزی مثلِ WebP همان تصویر را با حجمِ خیلی کمتر از JPGِ قدیمی ذخیره می‌کنند. برای عکس‌ها WebP، و برای آیکون‌ها همان SVGِ فصلِ قبل که اصلاً حجمِ چندانی ندارد.

از ترم ۴ یادت هست که با srcset می‌شد چند نسخهٔ هم‌عکس در اندازه‌های مختلف داد و مرورگر خودش مناسب‌ترین را برمی‌داشت؟ آنجا هم دقیقاً همین بحثِ سرعت بود: با srcset، تلفنِ کوچک نسخهٔ کوچک را می‌گیرد و مانیتورِ بزرگ نسخهٔ بزرگ را — هیچ‌کس بایتِ اضافه دانلود نمی‌کند.

💡 نکته: در DevTools بخشِ Network، ستونِ اندازه (Size) به تو می‌گوید هر تصویر چند کیلوبایت است. یک تصویرِ چند مگابایتی برای یک عکسِ کوچک، یعنی همان‌جا جای بهترشدن داری. حدس نزن — عدد را ببین.

۴. font-display: متنِ نامرئی وقتی فونت می‌آید#

وقتی یک فونتِ سفارشی (مثلِ یک فونتِ زیبای فارسی) را با @font-face بار می‌کنی، تا وقتی آن فایل نرسیده مرورگر باید تصمیم بگیرد با متن چه کند. رفتارِ پیش‌فرضش بد است: تا چند ثانیه متن را کاملاً نامرئی نگه می‌دارد تا فونت برسد. کاربر به یک صفحهٔ خالی زل می‌زند در حالی که متن آماده است.

font-display: swap این را عوض می‌کند: «متن را همین حالا با یک فونتِ ساده نشان بده، و هر وقت فونتِ اصلی رسید، عوضش کن». این‌طور کاربر هیچ‌وقت متنِ نامرئی نمی‌بیند:

@font-face {
  font-family: "Vazirmatn";
  src: url("vazirmatn.woff2") format("woff2");
  font-display: swap;
}

این هم‌خانوادهٔ همان مشکلِ CLS است: هر دو دربارهٔ چیزی‌اند که دیر می‌رسد. با width/height جای تصویر را رزرو کردی؛ با font-display: swap متن را در فاصلهٔ رسیدنِ فونت خوانا نگه می‌داری. عادتِ حرفه‌ای این است: هر منبعی که ممکن است دیر برسد را طوری بچین که کاربر در آن فاصله سردرگم یا معطل نشود.

💡 نکته: swap یک معاملهٔ کوچک دارد — لحظه‌ای که فونتِ اصلی می‌رسد، متن یک تکانِ ریز از فونتِ ساده به فونتِ اصلی می‌خورد. این تکانِ کوچک تقریباً همیشه بهتر از چند ثانیه متنِ کاملاً نامرئی است، ولی خوب است بدانی هست.

۵. CSSِ بی‌استفاده را حذف کن#

CSS هم دانلود می‌شود و هم باید خوانده شود. وقتی پروژه بزرگ می‌شود، پُر می‌شود از قاعده‌هایی که دیگر به هیچ عنصری نمی‌خورند — کلاسی که پاکش کردی ولی استایلش ماند، یک component که حذف شد ولی CSS اش نه. این‌ها بی‌فایده حجم اضافه می‌کنند و فایل را شلوغ نگه می‌دارند.

DevTools مرورگر ابزاری به‌نامِ Coverage دارد که دقیقاً همین را نشان می‌دهد: کدام خط‌های CSS در این صفحه اصلاً به‌کار نرفته‌اند. آن‌ها را که پیدا کردی، مطمئن شو واقعاً جای دیگری لازم نیستند و بعد حذفشان کن. کمترین CSSِ درست، بهترین CSS است.

🤖 با AI: یک فایلِ CSS و HTML به دستیار AI ات بده و بپرس «کدام selector ها در این HTML به هیچ عنصری نمی‌خورند؟». می‌تواند سریع مظنون‌ها را نشان دهد — ولی خودت با Coverageِ مرورگر تأییدش کن؛ AI ممکن است کلاسی را که جای دیگری استفاده می‌شود اشتباه «بی‌استفاده» بخواند.

۶. کد کامل#

اول index.html — دقت کن که تصویر هم width و height دارد تا جایش رزرو شود:

<!-- index.html -->
<!DOCTYPE html>
<html lang="fa" dir="rtl">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>سرعت</title>
    <link rel="stylesheet" href="styles.css">
  </head>
  <body>
    <main class="post">
      <p class="tag">سفرنامه</p>
      <h1>سه روز در جنگل</h1>
      <img
        class="hero"
        src="https://picsum.photos/id/1015/800/450"
        alt="رودخانه‌ای که از میان کوه‌های جنگلی می‌گذرد"
        width="800"
        height="450">
      <p>صبحِ روزِ اول با صدای رودخانه بیدار شدیم و تا شب بینِ درخت‌ها راه رفتیم.</p>
      <p>اینجا اینترنت نبود، و برای همین حواسمان فقط به مسیر و به هم بود.</p>
    </main>
  </body>
</html>

و styles.css — تصویر با width: 100% و height: auto روی هر عرضی جمع می‌شود، ولی نسبتش را از عددهای تگ می‌گیرد:

/* styles.css */
* {
  box-sizing: border-box;
}

:root {
  --bg: #f6edda;
  --card: #ffffff;
  --ink: #2c2114;
  --muted: #5a5342;
  --accent: #55731b;
  --line: #d8ccae;
}

body {
  margin: 0;
  font-family: Tahoma, sans-serif;
  background-color: var(--bg);
  color: var(--ink);
  padding: 40px;
  line-height: 1.9;
}

.post {
  max-width: 480px;
  margin-inline: auto;
  padding: 24px;
  background-color: var(--card);
  border: 1px solid var(--line);
  border-radius: 16px;
}

.tag {
  margin: 0;
  color: var(--accent);
  font-weight: 700;
}

.post h1 {
  margin: 4px 0 16px;
  font-size: 1.5rem;
}

.hero {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 12px;
}

چک کن: صفحه با Slow 3G هم آرام بار می‌شود: جای تصویر از اول رزرو است، متن سرِ جای نهایی‌اش نشسته، و موقعِ رسیدنِ تصویر هیچ‌چیز نمی‌پرد.

واژه‌های تازهٔ این فصل#

کلمه تلفظ به حروف فارسی یعنی چه
CLS سی‌اِل‌اِس جابه‌جاییِ ناگهانیِ چیدمان؛ وقتی صفحه موقعِ بارگذاری می‌پرد
font-display فونت دیسپلِی property که می‌گوید تا رسیدنِ فونت با متن چه کند
swap سواپ مقدارِ font-display که یعنی «فعلاً فونتِ ساده، بعد عوضش کن»
WebP وِب‌پی فرمتِ تصویرِ امروزی با حجمِ کمتر از JPG
Coverage کاوِرِج ابزارِ DevTools که CSS و کدِ بی‌استفادهٔ صفحه را نشان می‌دهد

تمرین‌ها

اول خودت فکر کن یا امتحان کن — بعد اینجا را باز کن.

در فصل بعد#

الان می‌توانی یک صفحه را سبک و آرام نگه داری. تا اینجا CSSِ ما کوچک بوده و همه‌چیز سرِ جایش. فصل بعد سراغِ چیزی می‌رویم که وقتی پروژه بزرگ می‌شود پیش می‌آید: معماریِ CSS. یاد می‌گیری چطور کلاس‌ها را طوری نام‌گذاری کنی که به‌هم نریزند، فرقِ کلاس‌های utility و semantic چیست، و مهم‌تر از همه چطور از «جنگِ specificity» فرار کنی — همان جایی که هر اصلاح به یک selectorِ سنگین‌تر نیاز پیدا می‌کند تا آخرش !important سبز می‌شود.

به آخر این فصل رسیدی!

اگر ساختی و جواب داد، این دکمه مال توست.