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

فصل ۹ از ۱۱

پیشرفت ترم
۰٪

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

متغیرهای خودت در CSS

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

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

تا حالا هر رنگ و هر اندازه را هرجا لازم بود، دوباره تایپ کرده‌ای. اگر رنگِ اصلیِ سایتت را ده‌جا نوشته باشی و بخواهی عوضش کنی، باید ده‌جا را پیدا کنی — و حتماً یکی را جا می‌اندازی. در این فصل یاد می‌گیری رنگ‌ها و اندازه‌های تکراری را یک‌بار با یک اسمِ دلخواهِ خودت تعریف کنی و همه‌جا صدایشان بزنی. به این‌ها custom property می‌گویند. آخر فصل یک theme کوچک داری که با عوض‌کردنِ یک خط، رنگِ کلِ صفحه عوض می‌شود — و همین دقیقاً همان چیزی است که بعدها dark mode را ممکن می‌کند.

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

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

  • یک custom property با --name تعریف کنی و با var() صدایش بزنی
  • برای وقتی که متغیر تعریف نشده، یک مقدارِ پیش‌فرض (fallback) بگذاری
  • فرق تعریف روی :root (کلِ صفحه) و روی یک عنصرِ خاص (فقط همان‌جا) را بفهمی
  • یک theme کوچک بسازی و رنگِ کلِ صفحه را با یک خط عوض کنی
  • بگویی چرا AI دربارهٔ CSS گاهی با اعتمادبه‌نفس اشتباه می‌گوید، و چطور خودت ردش کنی

قبل از شروع#

  • از فصل ۸: کارت با رنگ، حاشیه و سایه — همان کارت را این‌بار با متغیر می‌سازیم.
  • از فصل ۳: cascade و inheritance — custom property ها هم مثل رنگ، از بالا به پایین به فرزندها می‌رسند.
  • از فصل ۷: فرق block و inline — در بخش AI به‌کارش می‌بریم.

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

۱. مشکل: یک رنگ، ده جا#

فرض کن رنگِ اصلیِ سایتت #5a4bff (یک بنفشِ خوش‌رنگ) است. آن را هم برای دکمه نوشته‌ای، هم برای برچسب، هم برای لینک‌ها:

.btn {
  background-color: #5a4bff;
}

.tag {
  color: #5a4bff;
}

.link {
  color: #5a4bff;
}

حالا مدیرِ سایت می‌گوید «بنفش را دوست ندارم، سبزش کن». باید هر سه جا را پیدا کنی و عوض کنی. سه جا که ساده است؛ ولی در یک سایتِ واقعی همین رنگ شاید در چهل جا باشد. یکی را جا می‌اندازی، و یک دکمهٔ بنفش وسطِ یک سایتِ سبز باقی می‌ماند. مشکل این است که رنگِ #5a4bff اسم ندارد؛ فقط یک کدِ تکرارشده است که معنایش («رنگِ اصلیِ سایت») هیچ‌جا نوشته نشده.

۲. یک متغیر بساز: --name و var()#

راهِ حل این است که به آن رنگ یک اسم بدهی و بعد همه‌جا با آن اسم صدایش بزنی. این کار دو قدم دارد:

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

:root {
  --brand: #5a4bff;
}

--brand اسمی است که خودت انتخاب کرده‌ای؛ می‌توانست --main-color یا هرچیزِ دیگری باشد. این‌که با -- شروع شود اجباری است — این‌طور مرورگر می‌فهمد که این یک custom property است، نه یک property واقعیِ CSS.

قدم دوم — استفاده. هرجا آن رنگ را می‌خواهی، به‌جای کدِ رنگ می‌نویسی var(--brand):

.btn {
  background-color: var(--brand);
  color: #ffffff;
}

var(--brand) به مرورگر می‌گوید «هرچه --brand تعریف شده، همان را اینجا بگذار». حالا در آزمایشگاه دکمه را با var(--brand) بساز و ببین بنفش می‌شود.

حالا جادو را ببین: مقدارِ --brand را روی :root از #5a4bff به #1f9d55 (یک سبز) عوض کن. فقط همان یک خط. دکمه — و هر چیزِ دیگری که var(--brand) را نوشته باشد — یک‌جا سبز می‌شود. این همان چیزی است که در بخش قبل نداشتیم: یک منبعِ واحد که همه از آن تغذیه می‌کنند.

حالا بیا یک اشتباهِ خیلی رایج را عمداً مرتکب شویم تا وقتی واقعی‌اش برایت پیش آمد، بشناسی‌اش. اسمِ متغیر را در var() غلط تایپ کن:

.btn {
  background-color: var(--brnad);
  color: #ffffff;
}

نگاه کن: دکمه رنگش را کاملاً از دست داد — پس‌زمینه‌اش انگار پاک شد.

🔧 اگر کار نکرد: دقت کن، --brnad نوشته‌ایم نه --brand. چون هیچ متغیری به این اسم تعریف نشده، مرورگر نمی‌داند چه رنگی بگذارد، پس آن خط را بی‌سروصدا دور می‌ریزد و پس‌زمینه به حالتِ پیش‌فرضش (بی‌رنگ) برمی‌گردد. برخلافِ یک نام property غلط که معمولاً زود لو می‌رود، یک اسمِ متغیرِ غلط خطایی نشان نمی‌دهد؛ فقط ساکت کار نمی‌کند. راهِ حل: اسم را درست بنویس. اسمِ custom property باید مو‌به‌مو یکی باشد — حتی --Brand با --brand فرق دارد، چون به بزرگ و کوچکِ حروف حساس است.

اسم را به var(--brand) برگردان تا دکمه دوباره بنفش شود.

قبل از ادامه، جواب بده: اگر --brand را روی :root تعریف کنی ولی در ده جای CSS از var(--brand) استفاده کنی و بعد مقدارِ --brand را عوض کنی، چند جا تغییر می‌کند؟

هر ده جا، با هم، با یک تغییر. این کلِ فایدهٔ custom property است: یک منبع، هر تعداد مصرف‌کننده.

۳. مقدار پیش‌فرض: وقتی متغیر نباشد#

var() یک قابلیتِ دومِ مفید هم دارد: می‌توانی بعد از اسمِ متغیر، با یک ویرگول، یک مقدارِ پیش‌فرض بگذاری. اگر آن متغیر تعریف شده باشد، مقدارش استفاده می‌شود؛ اگر نه، مقدارِ پیش‌فرض:

.btn {
  background-color: var(--brand, #5a4bff);
}

این یعنی «--brand را بگذار؛ ولی اگر تعریف نشده بود، #5a4bff را بگذار». این یک تورِ نجات است برای وقتی مطمئن نیستی متغیر همیشه تعریف شده.

⚠️ مواظب باش: مقدارِ پیش‌فرض جای درست‌نوشتنِ اسم را نمی‌گیرد. اگر مثلِ بخشِ قبل اسم را --brnad غلط بنویسی و یک پیش‌فرض هم بگذاری، دکمه پیش‌فرض را نشان می‌دهد و انگار همه‌چیز درست است — ولی متغیرِ اصلی‌ات هیچ‌وقت به‌کار نرفته. پس اول اسم را درست بنویس؛ پیش‌فرض برای «شاید تعریف‌نشده باشد» است، نه برای پنهان‌کردنِ اشتباهِ تایپی.

۴. scope: کلِ صفحه یا فقط یک‌جا#

تا حالا متغیر را روی :root تعریف کردیم که یعنی «همه‌جای صفحه». ولی می‌توانی همان اسم را روی یک عنصرِ خاص هم تعریف کنی — و آنجا مقدارِ تازه فقط برای همان عنصر و فرزندهایش به‌کار می‌رود. چرا؟ چون custom property ها هم مثل بقیهٔ ارث در CSS از بالا به پایین می‌روند (همان inheritance از فصل ۳).

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

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

.card--warm {
  --brand: #e0663a;
}

دقت کن که قاعدهٔ .btn را اصلاً عوض نکردیم؛ هنوز همان background-color: var(--brand) است. ولی دکمه‌ای که داخلِ .card--warm باشد، --brand را از کارتِ نزدیک‌ترش می‌گیرد (نارنجی)، و دکمه‌ای که بیرونِ آن باشد، همان --brand از :root را (بنفش).

چک کن: دو دکمه می‌بینی — اولی بنفش، دومی نارنجی — با این‌که قاعدهٔ هر دو کاملاً یکی است. تنها فرق، این است که کارتِ دوم --brand خودش را دارد. این را «scope محلیِ» متغیر می‌گویند: مقدار فقط جایی که تعریفش کرده‌ای و پایین‌ترش زندگی می‌کند.

قبل از ادامه، جواب بده: اگر --brand را روی .card--warm تعریف کنی، آیا دکمهٔ کارتِ اول هم نارنجی می‌شود؟

نه. تعریفِ روی .card--warm فقط به خودش و فرزندهایش می‌رسد. کارتِ اول فرزندِ آن نیست، پس همچنان --brand را از :root می‌گیرد و بنفش می‌ماند.

۵. چرا این پایهٔ theme است#

حالا همه‌چیز را کنار هم می‌گذاریم. بالای CSS، یک دسته متغیر روی :root تعریف می‌کنیم — نه فقط رنگ، بلکه اندازهٔ گوشه و فاصله هم. این چند خط، «theme» سایت توست: یک‌جا، منظم، قابلِ عوض‌کردن. اول HTML:

<!-- index.html -->
<!DOCTYPE html>
<html lang="fa" dir="rtl">
  <head>
    <meta charset="UTF-8">
    <title>متغیرهای CSS</title>
    <link rel="stylesheet" href="styles.css">
  </head>
  <body>
    <article class="card">
      <h2>کارت اصلی</h2>
      <p>این کارت از رنگِ اصلیِ theme استفاده می‌کند.</p>
      <a class="btn" href="#">دنبال کن</a>
    </article>

    <article class="card card--warm">
      <h2>کارت ویژه</h2>
      <p>این کارت رنگِ خودش را دارد، ولی بقیهٔ theme ثابت مانده.</p>
      <a class="btn" href="#">دنبال کن</a>
    </article>
  </body>
</html>

و CSSِ کامل:

/* styles.css */
:root {
  --brand: #5a4bff;
  --ink: #172440;
  --muted: #55607a;
  --card-bg: #ffffff;
  --radius: 16px;
  --pad: 16px;
}

body {
  font-family: Tahoma, sans-serif;
  background-color: #eef1f7;
  padding: 40px;
}

.card {
  max-width: 320px;
  margin-bottom: 20px;
  padding: var(--pad);
  background-color: var(--card-bg);
  border: 1px solid #e4e8f0;
  border-radius: var(--radius);
  box-shadow: 0 12px 30px rgb(23 36 64 / 0.12);
}

.card h2 {
  margin: 0 0 8px;
  color: var(--ink);
}

.card p {
  margin: 0 0 12px;
  color: var(--muted);
  line-height: 1.9;
}

.btn {
  display: inline-block;
  padding: 8px 16px;
  border-radius: 10px;
  background-color: var(--brand);
  color: #ffffff;
  font-weight: 700;
}

.card--warm {
  --brand: #e0663a;
}

چک کن: دو کارتِ تمیز می‌بینی که رنگِ متن، گوشه‌ها و فاصله‌شان همه از متغیرهای بالای فایل می‌آید — و دکمهٔ کارتِ دوم نارنجی است. حالا این را امتحان کن: --card-bg را از #ffffff به #172440 (یک سرمه‌ایِ تیره) و --ink را به #f2f4f8 (روشن) عوض کن. کلِ صفحه تیره می‌شود، فقط با دو خط. این دقیقاً همان چیزی است که dark mode را می‌سازد — این را در ترم ۴ کامل یاد می‌گیریم، ولی موتورش همین است: یک theme از متغیرها، که با عوض‌کردنِ چند مقدار، کلِ ظاهر عوض می‌شود.

۶. همیار AI: کمکِ درست، اشتباهِ رایج#

این فصل یکی از چند فصلی است که کاملاً به AI می‌پردازیم. سه قانونِ ثابتِ کار با AI در کلِ این خودآموز اینهاست:

🤖 با AI: سه قانون را عیناً به خاطر بسپار: «اول خودت، بعد AI» — اول با معیارهای خودت تصمیم بگیر، بعد نظر AI را به‌عنوان نظرِ دوم بشنو، نه جوابِ نهایی. «خط به خط» — هر چیزی که AI می‌دهد را خط‌به‌خط بخوان و بفهم، هیچ‌وقت یک‌جا کپی نکن. «AI هم اشتباه می‌کند» — و همین الان یک نمونه‌اش را با هم می‌بینیم.

کاربردِ خوب. custom property ها دقیقاً همان‌جایی‌اند که AI می‌تواند یک جفت‌چشمِ دومِ خوب باشد — بعد از این‌که خودت CSS را نوشتی:

🤖 با AI: CSSِ خودت را به دستیار AI ات بده و بپرس: «کدام رنگ‌ها و اندازه‌ها در این فایل تکرار شده‌اند و بهتر است به custom property تبدیل شوند؟ برای هرکدام یک اسمِ معنادار پیشنهاد بده». بعد پیشنهادهایش را خودت پیاده کن و با چیزی که در آزمایشگاه می‌بینی چک کن: اگر تبدیل درست انجام شده باشد، صفحه باید دقیقاً مثل قبل دیده شود — چون فقط اسم عوض شده، نه مقدار. اگر چیزی تغییر کرد، یعنی یک‌جا اشتباه شده. این بهترین آزمونِ یک refactor است: ظاهر نباید تکان بخورد.

کاربردِ اشتباه. حالا نمونه‌ای که AI با اعتمادبه‌نفسِ کامل، اشتباه جواب می‌دهد. فرض کن می‌خواهی یک کارت را وسطِ عمودیِ صفحه بیاوری و از AI می‌پرسی «چطور این کارت را عمودی وسط بیاورم؟». خیلی وقت‌ها جوابی شبیه این می‌دهد:

.card {
  vertical-align: middle;
}

«فقط vertical-align: middle را اضافه کن، وسط می‌شود.» جمله‌اش قاطع و منطقی به‌نظر می‌رسد — «vertical» یعنی عمودی، «middle» یعنی وسط، پس باید کار کند. ولی در آزمایشگاه امتحانش کن: vertical-align: middle را به .card اضافه کن. هیچ اتفاقی نمی‌افتد. کارت یک میلی‌متر هم تکان نمی‌خورد.

چرا؟ با همان چیزی که در فصل ۷ یاد گرفتی می‌توانی ردش کنی. vertical-align فقط روی عنصرهای inline و inline-block (و خانه‌های جدول) کار می‌کند — کارش این است که این‌ها را نسبت به خطِ متنِ کنارشان بالا و پایین کند. ولی .card یک عنصرِ block است، و یک block در جریانِ عادی، vertical-align را کاملاً نادیده می‌گیرد. AI یک property واقعی را برداشته و در جای اشتباه پیشنهاد داده؛ اسمش فریب‌دهنده است، ولی کارش آن چیزی نیست که به‌نظر می‌رسد.

پس جوابِ درست چیست؟ وسط‌چینِ عمودیِ واقعیِ یک block با ابزاری به‌نامِ flexbox انجام می‌شود که این را در ترم ۳ کامل یاد می‌گیریم. نکتهٔ این فصل این نیست که چطور وسط‌چین کنی؛ نکته این است که AI جمله‌ای ساخت که درست به‌نظر می‌رسید ولی غلط بود، و تو با فهمِ خودت از block و inline توانستی ردش کنی. این بهترین درسِ «AI هم اشتباه می‌کند» است: AI دربارهٔ CSS مدام با اعتمادبه‌نفس اشتباه می‌گوید، و تنها محافظت، این است که هر پیشنهادش را در مرورگر ببینی. اول خودت، بعد AI.

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

کلمه تلفظ به حروف فارسی یعنی چه
custom property کاستِم پراپِرتی متغیرِ CSS که خودت با --name تعریف می‌کنی
var وَر تابعی که مقدارِ یک custom property را می‌خواند (var(--name))
fallback فال‌بک مقدارِ پیش‌فرض در var() برای وقتی متغیر تعریف نشده
root روت ریشهٔ صفحه؛ :root جایی است که متغیرهای سراسری را تعریف می‌کنی
scope اسکوپ محدوده‌ای که یک متغیر در آن زندگی می‌کند و به فرزندها می‌رسد
theme تِم مجموعهٔ متغیرهایی که ظاهرِ کلی سایت را می‌سازند

تمرین‌ها

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

در فصل بعد#

الان می‌توانی یک theme منظم بسازی. ولی وقتی CSS ات بزرگ شود، لحظه‌هایی پیش می‌آید که یک استایل «باید» اعمال شود ولی نمی‌شود، یا رنگی که انتظار داری ظاهر نمی‌شود — و حدس‌زدن جواب نیست. فصل بعد ابزارِ واقعیِ حرفه‌ای‌ها را باز می‌کنیم: DevTools. یاد می‌گیری قاعده‌ای که در cascade باخته و خط‌خورده را با چشمِ خودت ببینی، مقدارِ نهایی را در Computed بخوانی، و همان‌جا در مرورگر مقدارها را زنده عوض کنی. همان بدهیِ «چرا این استایل اعمال نشد؟» را آنجا نقد می‌کنیم.

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

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