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

فصل ۵ از ۱۱

پیشرفت ترم
۰٪

ترم ۲ · CSS: کنترل ظاهر

واحد و رنگ

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

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

آخر این فصل یک رشته نظرِ تودرتو داری — مثل بخش نظرهای زیر یک مطلب، که هر جواب کمی تو رفته و زیر نظرِ قبلی می‌نشیند. رنگ‌هایش را خودت انتخاب می‌کنی، اسم‌ها آبی‌اند، یک برچسبِ «نویسنده» رنگی دارد، و زمان‌ها کم‌رنگ و محوند. ولی مهم‌تر از ظاهرش، دو ابزارِ پایه را کنترل می‌کنی: واحد (اندازه‌ها را با چه چیزی بنویسی) و رنگ (رنگ را به چند شکل بنویسی).

مقایسهٔ دو راهِ اندازه‌گیری فونت: در سمتی، جعبه‌های تودرتو که هرکدام از قبلی بزرگ‌تر می‌شوند و مثل گلوله برف رشد می‌کنند؛ در سمتِ دیگر، همان جعبه‌ها که همه از یک خط‌کشِ ثابت اندازه گرفته‌اند و یک‌اندازه‌اند

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

  • فرق واحدِ مطلق (px) و نسبی (%، rem، em) را بگویی
  • برای اندازهٔ فونت rem را درست به‌کار ببری و بفهمی چرا معمولاً بهترین انتخاب است
  • یک رنگ را با hex، rgb()، hsl() و oklch() بنویسی
  • با opacity چیزی را نیمه‌شفاف کنی

قبل از شروع#

  • از فصل ۴: box-sizing، padding، border، margin و واحد px که آنجا سرسری معرفی شد؛ اینجا کاملش می‌کنیم.
  • از فصل ۳: cascade — یادت هست که وقتی دو قاعده به یک چیز مقدار می‌دهند، آخری برنده است؟ در بخش oklch دقیقاً به همین تکیه می‌کنیم.
  • از فصل ۲: class و از فصل ۱: color و background-color.

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

۱. واحد: مطلق در برابر نسبی#

هر اندازه‌ای در CSS — پهنا، فاصله، اندازهٔ فونت — یک عدد است به‌علاوهٔ یک واحد. واحدها دو دسته‌اند.

واحدِ مطلق، یعنی px: همیشه همان‌قدر است، به هیچ‌چیزِ دیگر وابسته نیست. 16px یعنی ۱۶ نقطه، تمام. صریح و قابل‌پیش‌بینی است.

واحدِ نسبی، یعنی درصدی یا برابری از یک چیزِ دیگر. مثلاً % نسبت به فضای در دسترس است. اگر به یک جعبه بگویی width: 50%، یعنی نصفِ عرضِ چیزی که داخلش نشسته. بیا سریع ببینیمش؛ در آزمایشگاه یک جعبهٔ آزمایشی بساز:

.demo {
  width: 50%;
  background-color: #2a6b93;
  height: 40px;
}

چک کن: جعبه دقیقاً تا نصفِ عرضِ صفحه کشیده می‌شود. حالا پنجرهٔ مرورگر (یا پنلِ آزمایشگاه) را پهن‌تر و باریک‌تر کن — جعبه هم با آن بزرگ و کوچک می‌شود، چون 50% به عرضِ صفحه وابسته است. اگر width را 200px بگذاری، هرچه پنجره را عوض کنی جعبه ثابت می‌ماند. این تفاوتِ مطلق و نسبی است.

۲. rem و em: دو واحدِ نسبی برای فونت#

برای اندازهٔ فونت دو واحدِ نسبیِ مهم داریم که خیلی شبیه‌اند و دقیقاً همین شباهت، تازه‌کارها را گیر می‌اندازد:

  • em نسبت به font-size ‌ی است که خودِ عنصر دارد (که معمولاً از پدرش به ارث برده). 1.25em یعنی «۱٫۲۵ برابرِ فونتِ فعلی».
  • rem نسبت به font-size ‌ی است که ریشهٔ صفحه (<html>) دارد. آن یکی هرچقدر تودرتو باشی ثابت می‌ماند.

مرورگر به‌طور پیش‌فرض به ریشه 16px می‌دهد. پس 1rem تقریباً همیشه 16px است، 1.5rem می‌شود 24px، و این عددها هرجای صفحه که باشی عوض نمی‌شوند. em اما بستگی دارد به اینکه کجا نشسته‌ای — و همین‌جاست که ماجرا جالب می‌شود.

💡 نکته: چرا اصلاً rem به‌جای px؟ چون بعضی کاربران برای راحتیِ چشم، اندازهٔ فونتِ پیش‌فرضِ مرورگرشان را بزرگ‌تر می‌کنند. اگر فونت‌ها را با rem نوشته باشی، همه‌شان با آن تنظیم بزرگ می‌شوند و صفحه‌ات خوانا می‌ماند. اگر با px نوشته باشی، ثابت می‌مانند و آن کاربر را نادیده گرفته‌ای.

بیا این را روی نظرهای واقعی ببینیم. اول ساختارِ رشته‌نظرها را در HTML بنویس — دقت کن هر جواب، یک <article class="comment"> است داخلِ نظرِ قبلی:

<!-- index.html -->
<!DOCTYPE html>
<html lang="fa" dir="rtl">
  <head>
    <meta charset="UTF-8">
    <title>نظرها</title>
    <link rel="stylesheet" href="styles.css">
  </head>
  <body>
    <article class="comment">
      <p class="name">نیما</p>
      <p class="tag">نویسنده</p>
      <p>این مقاله خیلی به‌دردم خورد، ممنون!</p>
      <p class="time">۲ ساعت پیش</p>

      <article class="comment">
        <p class="name">سارا</p>
        <p>موافقم، مخصوصاً بخش آخرش را دوست داشتم.</p>
        <p class="time">۱ ساعت پیش</p>

        <article class="comment">
          <p class="name">نیما</p>
          <p>خوشحالم که مفید بود.</p>
          <p class="time">۴۰ دقیقه پیش</p>
        </article>
      </article>
    </article>
  </body>
</html>

و یک استایلِ ساده بهش بده تا هر نظر یک جعبهٔ سفید با کمی فاصله باشد:

.comment {
  background-color: #ffffff;
  border: 1px solid #d9cbaa;
  padding: 16px;
  margin-top: 16px;
}

چک کن: سه نظر می‌بینی که هرکدام کمی تو رفته و زیرِ نظرِ قبلی نشسته‌اند — چون هر جواب داخلِ نظرِ قبلی است و padding ‌اش آن را تو می‌بَرد. اندازهٔ متنِ همه یکسان است. حالا با em این آرامش را به‌هم می‌زنیم.

۳. وقتی em از دستت در می‌رود#

فرض کن می‌خواهی نظرها کمی بزرگ‌تر از حالت عادی باشند. طبیعی است که بنویسی «۱٫۲۵ برابرِ فونت». به .comment این را بده:

.comment {
  font-size: 1.25em;
  background-color: #ffffff;
  border: 1px solid #d9cbaa;
  padding: 16px;
  margin-top: 16px;
}

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

🔧 اگر کار نکرد: این عمدی است و باید همین بزرگ‌شدنِ پلکانی را ببینی. دلیلش در تعریفِ em است: em نسبت به فونتِ خودِ عنصر است، و هر نظر فونتش را از نظرِ پدرش می‌گیرد. پس محاسبه اینطور زنجیر می‌شود: نظرِ اول 1.25 × 16 = 20؛ نظرِ دوم 1.25 × ۲۰ = 25؛ نظرِ سوم 1.25 × ۲۵ = 31. هر لایه 1.25 برابرِ لایهٔ قبل می‌شود، نه ۱٫۲۵ برابرِ ریشه — به این می‌گویند compounding، مثل گلولهٔ برفی که هر دور بزرگ‌تر می‌شود.

راهِ حل این است که به‌جای «۱٫۲۵ برابرِ پدرم» بگویی «۱٫۲۵ برابرِ ریشه» — یعنی em را با rem عوض کنی. rem هرچقدر هم تودرتو باشی به همان ریشه نگاه می‌کند، پس دیگر زنجیر نمی‌شود:

.comment {
  font-size: 1rem;
  background-color: #ffffff;
  border: 1px solid #d9cbaa;
  padding: 16px;
  margin-top: 16px;
}

چک کن: حالا هر سه نظر دقیقاً هم‌اندازه‌اند، مهم نیست چقدر تودرتو باشند. مقدار را به 1.25rem عوض کن: همه با هم بزرگ می‌شوند ولی باز هم هم‌اندازه می‌مانند — چون هر سه از یک ریشه اندازه می‌گیرند. این است فرقِ rem و em.

💡 نکته: پس em بی‌فایده است؟ نه. em دقیقاً وقتی خوب است که بخواهی چیزی نسبت به فونتِ همان عنصر مقیاس بگیرد — مثلاً padding ‌ی که باید با بزرگ‌شدنِ متن، بزرگ شود. قاعدهٔ سرانگشتی: برای اندازهٔ فونت rem، برای فاصله‌هایی که باید با فونت هماهنگ بمانند em.

قبل از ادامه، جواب بده: اگر ریشه 16px باشد و یک عنصر font-size: 2rem بگیرد، چند پیکسل می‌شود؟ (جواب: 32px — و مهم نیست کجای صفحه باشد.)

۴. رنگ: سه راه برای نوشتنِ یک رنگ#

تا حالا رنگ‌ها را با کدهایی مثل #2a6b93 نوشتیم. به این hex می‌گویند: یک # و شش رقمِ شانزده‌شانزدهی که به‌ترتیب مقدارِ قرمز، سبز و آبی‌اند. کوتاه است ولی برای آدم خواندنی نیست — با نگاه به #2a6b93 نمی‌فهمی چه رنگی است.

یک رنگِ آبیِ یکسان که با سه نمودارِ مختلف توصیف شده: سه نوارِ قرمز/سبز/آبی، یک چرخِ رنگ، و یک نوارِ روشنایی که همه به یک مربعِ رنگیِ مرکزی می‌رسند

همان رنگ را می‌شود جورِ دیگر هم نوشت. rgb() همان سه مقدارِ قرمز/سبز/آبی را با عددِ معمولی (۰ تا ۲۵۵) می‌گیرد:

.name {
  color: rgb(42 107 147);
}

rgb(42 107 147) دقیقاً همان #2a6b93 است، فقط خواناتر. ولی هنوز هم با دیدنِ سه عدد نمی‌فهمی رنگ چیست. اینجاست که hsl() کمک می‌کند: رنگ را با سه چیزِ قابل‌تصور می‌گوید — hue (نوعِ رنگ، ۰ تا ۳۶۰ روی چرخِ رنگ)، saturation (چقدر پُررنگ) و lightness (چقدر روشن):

.name {
  color: hsl(203 56% 37%);
}

چک کن: هر سه نوشته — #2a6b93، rgb(42 107 147) و hsl(203 56% 37%) — یک رنگِ آبیِ یکسان می‌دهند؛ یکی‌یکی امتحان کن و ببین اسمِ نظرها عوض نمی‌شود. مزیتِ hsl وقتی معلوم می‌شود که بخواهی رنگ را تنظیم کنی: عددِ اولِ hsl را از 203 به 10 ببر تا آبی به قرمز برود، یا عددِ آخر را از 37% به 70% ببر تا همان آبی روشن‌تر شود. با hex این کارها حدس‌وگمان است؛ با hsl مثل چرخاندنِ چند پیچ است.

۵. oklch و یک fallback واقعی#

یک راهِ تازه‌تر و بهترِ نوشتنِ رنگ هم هست: oklch(). مثل hsl با روشنی و رنگ و شدت کار می‌کند، ولی طوری ساخته شده که به چشمِ آدم نزدیک‌تر است — وقتی روشنی را کم‌وزیاد می‌کنی، تغییرش یکنواخت به‌نظر می‌رسد، نه پرشی. و به رنگ‌های زنده‌تری دسترسی می‌دهد که در rgb قدیمی جا نمی‌شدند.

oklch امروز در مرورگرهای امروزی کار می‌کند، ولی ممکن است کاربری مرورگرِ خیلی قدیمی داشته باشد که آن را نشناسد. برای این‌جور جاها یک fallback می‌نویسیم: اول رنگ را به یک شکلِ قدیمی و مطمئن (hex) می‌نویسیم، بعد در خطِ بعد به oklch. به برچسبِ «نویسنده» این را بده:

.tag {
  color: #ffffff;
  background-color: #a36207;              /* fallback: rang-e sRGB */
  background-color: oklch(55% 0.11 66);   /* modern: gamut-e vasi'-tar */
  padding: 2px 8px;
}

اینجا دقیقاً از cascadeِ فصل ۳ استفاده می‌کنیم: دو قاعده به یک property مقدار می‌دهند و آخری برنده است — به‌شرطی که مرورگر آن را بفهمد. مرورگرِ امروزی هر دو خط را می‌خواند و چون oklch را می‌فهمد، خطِ دومی برنده می‌شود. مرورگرِ خیلی قدیمی خطِ oklch را نمی‌فهمد و دورش می‌اندازد، پس همان hex برایش می‌ماند. هیچ‌کس رنگِ خراب نمی‌بیند.

چک کن: برچسبِ «نویسنده» یک پس‌زمینهٔ نارنجی‌‌‌طلایی می‌گیرد. خطِ oklch را موقتاً پاک کن و ببین رنگ به همان نارنجیِ hex برمی‌گردد — همان چیزی که یک مرورگرِ قدیمی می‌بیند.

⚠️ مواظب باش: ترتیب مهم است. اگر hex را بعد از oklch بنویسی، مرورگرِ امروزی هم آخری را برمی‌دارد و رنگِ بهتر را از دست می‌دهی. همیشه fallback ‌ی اول، نسخهٔ بهتر بعد.

نوشتنِ این نسخه‌های مختلفِ یک رنگ، دقیقاً جایی است که AI می‌تواند کارت را راه بیندازد:

🤖 با AI: از دستیار AI ات بخواه یک رنگِ hex (مثلاً #a36207) را به hsl و oklch تبدیل کند. بعد هر سه را در آزمایشگاه امتحان کن و با چشمت چک کن که واقعاً یک رنگ‌اند — چون AI در تبدیلِ رنگ گاهی عددِ کمی پرت می‌دهد، و تنها راهِ مطمئن‌شدن، دیدنِ نتیجه در مرورگر است، نه اعتماد به عددِ AI.

۶. opacity: نیمه‌شفاف کردن#

گاهی می‌خواهی چیزی کم‌رنگ و کم‌اهمیت به‌نظر برسد، مثل زمانِ زیر هر نظر. opacity کلِ عنصر را شفاف می‌کند: 1 یعنی کاملاً تو‌پُر، 0 یعنی کاملاً نامرئی، و بینشان نیمه‌شفاف. به زمان‌ها این را بده:

.time {
  color: #2c2114;
  opacity: 0.55;
}

چک کن: زمان‌ها («۲ ساعت پیش» و بقیه) کم‌رنگ و محو شده‌اند و دیگر با متنِ اصلی رقابت نمی‌کنند. عدد 0.55 را به 0.2 ببر تا تقریباً ناپدید شوند، بعد به 1 تا کاملاً برگردند.

کد کاملِ این فصل#

فایلِ HTML همان است که در بخش ۲ نوشتی. این هم styles.cssِ کامل:

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

body {
  background-color: #f6edda;
}

.comment {
  font-size: 1rem;
  background-color: #ffffff;
  border: 1px solid #d9cbaa;
  padding: 16px;
  margin-top: 16px;
}

.name {
  color: #2a6b93;
}

.tag {
  color: #ffffff;
  background-color: #a36207;              /* fallback: rang-e sRGB */
  background-color: oklch(55% 0.11 66);   /* modern: gamut-e vasi'-tar */
  padding: 2px 8px;
}

.time {
  color: #2c2114;
  opacity: 0.55;
}

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

کلمه تلفظ به حروف فارسی یعنی چه
unit یونیت واحدِ اندازه که بعد از عدد می‌آید (px, rem, …)
px پیکسل واحدِ مطلق؛ همیشه ثابت
rem رِم واحدِ نسبی به font-size ‌ی ریشهٔ صفحه (<html>)
em اِم واحدِ نسبی به font-size ‌ی خودِ عنصر
font-size فونت‌سایز اندازهٔ متن
hex هِکس رنگ به‌شکلِ # و شش رقم شانزده‌شانزدهی
rgb آر‌جی‌بی رنگ با سه مقدارِ قرمز، سبز، آبی
hsl اچ‌اس‌ال رنگ با hue، saturation، lightness
oklch اُکِل‌سی‌اچ راهِ تازه‌ترِ نوشتن رنگ، نزدیک‌تر به چشمِ آدم
fallback فال‌بک نسخهٔ پشتیبان برای وقتی مرورگر نسخهٔ اصلی را نمی‌فهمد
opacity اوپَسیتی میزانِ شفافیتِ کلِ عنصر، از 0 تا 1

تمرین‌ها

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

در فصل بعد#

الان واحدها و رنگ را در دست داری. تا اینجا متن‌ها با فونتِ پیش‌فرضِ مرورگر بودند — که مخصوصاً برای فارسی همیشه زیبا و خوانا نیست. فصل بعد سراغ تایپوگرافی می‌رویم: با font-family فونت را عوض می‌کنیم، با @font-face یک فونتِ فارسیِ درست‌وحسابی را بار می‌کنیم، و با line-height به متنِ فارسی جای نفس‌کشیدن می‌دهیم.

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

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