مدیریت فایل‌ها در پروژه‌های بزرگ؛ وقتی یه فایل CSS دیگه کافی نیست

تو سه درس قبل با نام‌گذاری (BEM)، فلسفه‌ی سازماندهی کلاس‌ها (Component vs Utility) و مدیریت مقادیر (Design Tokens) آشنا شدیم. اما یه سؤال عملی دیگه هم باقی می‌مونه: این همه CSS رو دقیقاً تو کدوم فایل‌ها بنویسیم؟ یه پروژه‌ی کوچیک شاید با یه فایل styles.css کنار بیاد، ولی یه پروژه‌ی بزرگ با صدها کامپوننت، بدون یه ساختار فایل منطقی، خیلی زود غیرقابل‌مدیریت می‌شه.

مشکل یه فایل غول‌پیکر

وقتی همه‌چیز تو یه فایل styles.css با چند هزار خط باشه، چند مشکل عملی پیش میاد: پیدا کردن استایل یه کامپوننت خاص وقت‌گیر می‌شه، چند نفر که هم‌زمان رو فایل کار می‌کنن مدام با تعارض (conflict) تو Git روبه‌رو می‌شن، و فهمیدن این‌که یه بخش از کد اصلاً هنوز استفاده می‌شه یا نه (همون بحث CSS بلااستفاده از فصل پرفورمنس) خیلی سخت‌تره.

الگوی رایج: تقسیم بر اساس نوع محتوا

یکی از قدیمی‌ترین و رایج‌ترین روش‌های سازماندهی، تقسیم فایل‌ها بر اساس نقششونه؛ این ساختار معمولاً با ابزارهایی مثل Sass پیاده‌سازی می‌شه که اجازه می‌ده چند فایل رو با @use یا @forward به هم وصل کنید (نه @import خام تو مرورگر که تو فصل پرفورمنس دیدیم چرا مشکل‌ساز بود):

/* styles/
   ├── tokens.css       (رنگ، فاصله، فونت؛ از درس قبل)
   ├── reset.css        (ریست پایه)
   ├── base.css         (استایل تگ‌های خام مثل body، a، h1)
   ├── components/
   │   ├── card.css
   │   ├── button.css
   │   └── modal.css
   ├── layout/
   │   ├── header.css
   │   └── footer.css
   └── main.css          (نقطه‌ی ورود؛ همه رو به هم وصل می‌کنه) */

هر فایل تو components/ فقط مسئول یه کامپوننت مستقله (مثل همون block هایی که تو درس BEM دیدیم)؛ یعنی اگه بخواید استایل .card رو پیدا کنید، دقیقاً می‌دونید باید سراغ کدوم فایل برید، بدون نیاز به جست‌وجو تو کل پروژه.

ترتیب فایل‌ها چرا مهمه؟

تو این ساختار، ترتیبی که فایل‌ها به هم وصل می‌شن معمولاً از عمومی‌ترین به خاص‌ترین حرکت می‌کنه: اول token ها (که همه‌چیز بهشون وابسته‌ست)، بعد reset و base (استایل پایه‌ی تگ‌ها)، و در آخر کامپوننت‌ها. این ترتیب دقیقاً همون منطق cascade که تو فصل ۲ دیدیم رو رعایت می‌کنه: قوانین عمومی‌تر باید زودتر بیان تا قوانین خاص‌تر (کامپوننت‌ها) بتونن راحت override شون کنن، بدون نیاز به specificity غیرضروری بالا.

ترکیب با Cascade Layers (فصل ۲۱)

تو فصل ۲۱ با @layer آشنا شدیم؛ این ابزار دقیقاً برای همین نوع سازماندهی طراحی شده و می‌شه مستقیم با ساختار فایل بالا هماهنگش کرد:

@layer tokens, reset, base, components, utilities;

@import url("tokens.css") layer(tokens);
@import url("reset.css") layer(reset);
@import url("base.css") layer(base);
@import url("components/card.css") layer(components);
@import url("components/button.css") layer(components);

مزیت این ترکیب اینه که حتی اگه ترتیب فیزیکی فایل‌ها تو پروژه اشتباه بشه (مثلاً یکی از توسعه‌دهنده‌ها یه import رو جابه‌جا کنه)، لایه‌بندی از قبل تعریف‌شده تضمین می‌کنه اولویت درست همیشه حفظ بشه؛ برخلاف حالت بدون layer که ترتیب فیزیکی فایل‌ها مستقیم رو نتیجه‌ی نهایی اثر می‌ذاره.

یه کامپوننت، یه فایل: چرا این الگو تو پروژه‌های کامپوننتی رایجه

تو پروژه‌هایی که با framework های جاوااسکریپتی (React، Vue) ساخته می‌شن، یه الگوی متفاوت هم خیلی رایجه: هر کامپوننت جاوااسکریپتی، فایل CSS خودش رو کنار خودش داره:

/* components/
   ├── Card.jsx
   ├── Card.css
   ├── Button.jsx
   └── Button.css */

مزیت اصلی این روش اینه که وقتی یه کامپوننت حذف می‌شه، فایل CSS مربوطش هم به‌راحتی قابل‌شناسایی و حذفه (چون کنار همدیگه‌ن)؛ برخلاف ساختار قبلی که باید دستی بگردید ببینید آیا یه بخش از components/card.css هنوز جایی استفاده می‌شه یا نه. این الگو معمولاً با ابزارهای build خاصی (مثل CSS Modules یا styled-components) همراه می‌شه که هر فایل CSS رو فقط به همون کامپوننت محدود می‌کنن (scoping خودکار)، ولی اصل مفهومی‌اش (نزدیک نگه‌داشتن CSS به کامپوننت مربوطه) کاملاً مستقل از این ابزارهاست.

فایل‌های خیلی کوچیک در برابر فایل‌های خیلی بزرگ: تعادل درست کجاست؟

تو فصل پرفورمنس دیدیم که تقسیم بیش‌ازحد فایل‌ها هزینه‌ی درخواست شبکه داره (البته اون بحث بیشتر مربوط به حالت بدون bundler بود). از نظر سازماندهی هم همین تعادل صادقه: اگه هر کلاس رو تو یه فایل جدا بذارید، پیمایش پروژه غیرممکن می‌شه؛ اگه همه‌چیز تو یه فایل باشه، همون مشکل فایل غول‌پیکر برمی‌گرده. قاعده‌ی عملی رایج: هر فایل باید نماینده‌ی یه واحد معنایی منسجم باشه (یه کامپوننت، یا یه گروه کوچیک از کامپوننت‌های خیلی مرتبط)، نه یه ویژگی تنها و نه کل سایت.

مستندسازی: یه بخش که اغلب فراموش می‌شه

تو پروژه‌های بزرگ، صرفاً تقسیم درست فایل‌ها کافی نیست؛ یه توضیح کوتاه بالای هر فایل درباره‌ی هدفش، یا یه فایل README که ساختار کلی رو توضیح بده، برای اعضای جدید تیم خیلی وقت صرفه‌جویی می‌کنه:

/* components/card.css
   استایل کامپوننت کارت محصول.
   وابسته به: tokens.css (برای رنگ و فاصله)
   استفاده می‌شه در: صفحه‌ی محصولات، صفحه‌ی جست‌وجو */

.card { }
نکته مهم: هیچ ساختار فایل «درست مطلق» وجود نداره؛ انتخاب بین ساختار مبتنی بر نوع (type-based، مثل مثال اول) و ساختار مبتنی بر کامپوننت (component-based، مثل الگوی دوم) بیشتر به این بستگی داره که پروژه از چه ابزارهایی استفاده می‌کنه و تیم به چه سبکی عادت داره.

اشتباه رایج: نداشتن هیچ قانون مشخص و رشد ارگانیک بی‌نظم

یه اشتباه خیلی رایج تو پروژه‌های بزرگ اینه که از همون اول هیچ ساختار مشخصی تعریف نمی‌شه، و هر توسعه‌دهنده هر فایل جدید رو هرجا که فکر می‌کنه درسته اضافه می‌کنه. نتیجه‌ی این رشد بی‌نظم، بعد از چند ماه، یه پروژه‌ست که هیچ‌کس (حتی خود نویسنده‌ی اصلی) نمی‌دونه یه استایل خاص رو کجا باید پیدا کنه یا اضافه کنه. تعریف یه ساختار ساده از همون روز اول، حتی اگه کامل نباشه، همیشه بهتر از نبود هر ساختاریه.

جمع‌بندی

پروژه‌های بزرگ CSS معمولاً یکی از دو الگوی اصلی رو دنبال می‌کنن: تقسیم بر اساس نوع (tokens، reset، base، components) یا تقسیم بر اساس کامپوننت (هر کامپوننت با فایل CSS کنار خودش). هر دو رویکرد رو می‌شه با Cascade Layers ترکیب کرد تا اولویت cascade مستقل از ترتیب فیزیکی فایل‌ها باشه. مهم‌تر از انتخاب دقیق الگو، داشتن یه قانون ثابت و مستندشده از همون ابتدای پروژه‌ست، نه نبود هر نظمی.