پرش به مطلب اصلی

Clean Code

مقدمه

در این فاز مجموعه‌ای از Best Practiceها و Anti-patternها را جمع‌آوری کرده‌ایم تا شما بتوانید از آن‌ها برای نوشتن کد تمیز کمک بگیرید. سعی می‌کنیم هم‌زمان با پیشرفت شما در این دوره و همچنین سپری‌شدن دوره‌های آینده، این مجموعه را توسعه دهیم تا با گذشت زمان تبدیل به مرجع مناسبی برای شما و دیگر توسعه‌دهندگان شود.

  • نوشتن کد تمیز چه مزایایی دارد؟
  • از چه اصول و از چه تکنیک‌هایی برای دست‌یابی به کد تمیز می‌توان استفاده کرد؟
  • کد تمیز به طور خاص در فرانت‌اند دارای چه ویژگی‌هایی است؟

یادگیری

Clean Code

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

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


Code Smells

یکی از راه‌های افزایش کیفیت کد، پیدا کردن نشانه‌های کد کثیف و بازنویسی آن‌هاست. به این نشانه‌ها Code Smell می‌گوییم.

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


S.O.L.I.D

یکی از مهم‌ترین مجموعه اصول در برنامه‌نویسی، اصول پنج‌گانهٔ SOLID می‌باشد که عبارت است از:

  1. Single Responsibility
  2. Open for Extension/Closed for Modification
  3. Liskov Substitution
  4. Interface Segregation
  5. Dependency Inversion

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


Refactoring Techniques

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

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


Open-Source

README.md

فایل README.md معرفی‌نامۀ پروژۀ شماست. تمام مواردی که یک کاربر بیرونی احتیاج دارد در مورد پروژه بداند باید در این فایل آورده شوند یا حداقل به جایی که در مورد آن‌ها توضیح داده شده است، اشاره شود.

پیشنهاد می‌کنیم موارد زیر را حتماً در این فایل بیاورید:

  • نام پروژه
  • لوگو شخص، تیم یا شرکت
  • لینک به سایت اصلی پروژه
  • وضعیت CI/CD، بالا بودن سرورها، پاس‌شدن تست‌ها و موارد مشابه
  • توضیحی چند خطی و کوتاه در مورد کاربرد پروژه
  • اهداف پروژه و نیازهایی که می‌تواند برطرف کند
  • پیش‌نیازهای استفاده از پروژه
  • لینک دانلود فایل نهایی/اجرایی پروژه
  • باگ‌های احتمالی و مواردی که کاربر باید به آن‌ها توجه کند
  • قابلیت‌های فعلی و نحوۀ استفاده از آن‌ها
  • مستندات کامل یا لینک به جایی که آن‌ها را قرار داده‌اید
  • برنامه‌های آینده و قابلیت‌هایی که می‌خواهید به پروژه اضافه کنید
  • شرایط پشتیبانی و نحوۀ ثبت درخواست
  • توضیح اجمالی، دعوت به مشارکت و لینک به CONTRIBUTING.md
  • لینک به صفحات پروژه در شبکه‌های اجتماعی
  • معرفی اجمالی توسعه‌دهندگان پروژه و لینک به صفحات مرتبط آن‌ها در شبکه‌های اجتماعی

CONTRIBUTING.md

در دنیای Open-Source، آدم‌های زیادی پیدا می‌شوند که به طور رایگان بخواهند به شما کمک کنند؛ بنابراین شما باید با فراهم‌کردن فایل CONTRIBUTING.md مناسب، آن‌ها را در این راه یاری کنید.

پیشنهاد می‌کنیم موارد زیر را حتماً در این فایل بیاورید:

  • پیشنهاد برای چک‌کردن لیست Issueهای فعلی و جلوگیری از ثبت Issue تکراری
  • لیست قابلیت‌هایی که دوست دارید توسط دیگران به پروژه اضافه شوند
  • لیست قابلیت‌هایی که دوست ندارید به پروژه اضافه شوند
  • لیست باگ‌هایی که دوست دارید توسط دیگران حل شوند
  • لیست باگ‌هایی که از وجود آن‌ها باخبرید اما قصد برطرف‌کردن آن‌ها را ندارید
  • مراحل ثبت باگ، درخواست امکانات جدید، ارتباط با توسعه‌دهندگان و موارد مشابه
  • پیش‌نیازهای راه‌اندازی پروژه بر روی سیستم شخص
  • نحوۀ Cloneکردن و راه‌اندازی پروژه به صورت خط به خط با بیشترین جزئیات ممکن
  • قوانین کدنویسی که در پروژه رعایت می‌کنید
  • قواعد نوشتن Commit Message

IDE

استفاده از Text Editorهایی مانند VS Code فقط برای پروژه‌های کوچک مناسب است. برای پروژه‌های بزرگ، مخصوصاً پروژه‌هایی که به صورت تیمی آن‌ها را توسعه می‌دهید، حتماً از WebStorm استفاده کنید.

ESLint

برای فعال‌کردن تنظیمات ESLint مراحل زیر را طی کنید:

  1. از بالا-چپ بر روی File و سپس Settings کلیک کنید تا پنجرۀ تنظیمات باز شود
  2. عبارت ESLint را در باکس بالا-چپ جست‌وجو کنید
  3. از قسمت سمت چپ بر روی گزینۀ Languages & Frameworks > JavaScript > Code Quality Tools > ESLint کلیک کنید
  4. از قسمت سمت راست تیک گزینۀ Automatic ESLint configuration را فعال کنید

Prettier

برای فعال‌کردن تنظیمات Prettier مراحل زیر را طی کنید:

  1. از بالا-چپ بر روی File و سپس Settings کلیک کنید تا پنجرۀ تنظیمات باز شود
  2. عبارت Prettier را در باکس بالا-چپ جست‌وجو کنید
  3. از قسمت سمت چپ بر روی گزینۀ Languages & Frameworks > JavaScript > Prettier کلیک کنید
  4. از قسمت سمت راست عبارت مقابل Run for files را به عبارت زیر تغییر دهید
    {**/*,*}.{*}
  5. تیک گزینۀ On save را فعال کنید

Stylelint

معمولاً هر شخص زمانی که کد CSS می‌نویسد، برای آن قواعدی در نظر می‌گیرد؛ به‌عنوان مثال ترتیب و اولویت Propertyهای مختلف یا استفاده از یک فرمت رنگ خاص. اما زمانی که به صورت تیمی بر روی پروژه‌ای کار می‌کنید، بهتر است این قواعد به صورت عمومی اعمال شوند تا کدی که در نهایت نوشته می‌شود برای همه قابل‌فهم و در یک چارچوب باشد. Stylelint ابزاری است که به ما امکان تعریف چنین قواعدی را می‌دهد.

Setup

برای افزودن Stylelint به پروژۀ انگولاری خود، از دستور زیر استفاده کنید:

npm install -D stylelint stylelint-config-standard-scss stylelint-config-prettier-scss stylelint-order postcss

با اجرای دستور بالا علاوه بر پکیج stylelint چند پکیج دیگر نیز به پروژه اضافه می‌شوند که به ترتیب مربوط به پشتیبانی از SCSS، جلوگیری از تداخل با Prettier، ترتیب Propertyها و PostCSS می‌باشند.

برای تنظیم قواعد، یک فایل با نام stylelintrc.json. بسازید. ما پیشنهاد می‌کنیم از قواعد زیر استفاده کنید:

{
"extends": ["stylelint-config-standard-scss", "stylelint-config-prettier-scss"],
"plugins": ["stylelint-order"],
"rules": {
"color-named": "never",
"color-no-hex": true,
"custom-property-empty-line-before": null,
"declaration-block-no-redundant-longhand-properties": null,
"declaration-empty-line-before": null,
"no-descending-specificity": null,
"no-empty-source": null,
"order/order": ["custom-properties", "declarations", "rules"],
"order/properties-order": [
{
"groupName": "reset",
"emptyLineBefore": "always",
"noEmptyLineBetween": true,
"properties": ["all"]
},
{
"groupName": "content",
"emptyLineBefore": "always",
"noEmptyLineBetween": true,
"properties": ["content"]
},
{
"groupName": "parent-related",
"emptyLineBefore": "always",
"noEmptyLineBetween": true,
"properties": [
"order",
"flex",
"flex-grow",
"flex-shrink",
"grid-area",
"align-self",
"justify-self",
"counter-increment",
"counter-reset"
]
},
{
"groupName": "color",
"emptyLineBefore": "always",
"noEmptyLineBetween": true,
"properties": [
"background",
"background-attachment",
"background-blend-mode",
"background-clip",
"background-color",
"background-image",
"background-origin",
"background-position",
"background-repeat",
"background-size",
"color",
"accent-color",
"fill",
"box-shadow",
"mix-blend-mode",
"opacity",
"visibility"
]
},
{
"groupName": "filter",
"emptyLineBefore": "always",
"noEmptyLineBetween": true,
"properties": ["filter", "backdrop-filter"]
},
{
"groupName": "mask",
"emptyLineBefore": "always",
"noEmptyLineBetween": true,
"properties": [
"mask",
"mask-clip",
"mask-composite",
"mask-image",
"mask-mode",
"mask-origin",
"mask-position",
"mask-repeat",
"mask-size",
"mask-type"
]
},
{
"groupName": "box",
"emptyLineBefore": "always",
"noEmptyLineBetween": true,
"properties": ["direction", "box-sizing"]
},
{
"groupName": "position",
"emptyLineBefore": "always",
"noEmptyLineBetween": true,
"properties": [
"position",
"isolation",
"inset",
"inset-block",
"inset-block-start",
"inset-block-end",
"inset-inline",
"inset-inline-start",
"inset-inline-end",
"top",
"right",
"bottom",
"left",
"overflow",
"overflow-block",
"overflow-inline",
"overflow-x",
"overflow-y",
"overflow-wrap",
"z-index"
]
},
{
"groupName": "display",
"emptyLineBefore": "always",
"noEmptyLineBetween": true,
"properties": [
"display",
"flex-basis",
"flex-direction",
"flex-flow",
"flex-wrap",
"grid",
"grid-auto-columns",
"grid-auto-flow",
"grid-auto-rows",
"grid-column",
"grid-column-end",
"grid-column-gap",
"grid-column-start",
"grid-row",
"grid-row-end",
"grid-row-gap",
"grid-row-start",
"grid-template",
"grid-template-areas",
"grid-template-columns",
"grid-template-rows",
"columns",
"place-content",
"place-items",
"align-content",
"align-items",
"justify-content",
"justify-items",
"gap",
"grid-gap",
"column-gap",
"row-gap"
]
},
{
"groupName": "list",
"emptyLineBefore": "always",
"noEmptyLineBetween": true,
"properties": ["list-style", "list-style-image", "list-style-position", "list-style-type"]
},
{
"groupName": "size",
"emptyLineBefore": "always",
"noEmptyLineBetween": true,
"properties": [
"min-block-size",
"block-size",
"max-block-size",
"min-inline-size",
"inline-size",
"max-inline-size",
"min-width",
"width",
"max-width",
"min-height",
"height",
"max-height",
"aspect-ratio",
"resize",
"object-fit",
"object-position",
"scroll-behavior"
]
},
{
"groupName": "scroll",
"emptyLineBefore": "always",
"noEmptyLineBetween": true,
"properties": ["scroll-behavior"]
},
{
"groupName": "box-model",
"emptyLineBefore": "always",
"noEmptyLineBetween": true,
"properties": [
"margin",
"margin-block",
"margin-block-start",
"margin-block-end",
"margin-inline",
"margin-inline-start",
"margin-inline-end",
"margin-top",
"margin-right",
"margin-bottom",
"margin-left",
"padding-block",
"padding-block-start",
"padding-block-end",
"padding-inline",
"padding-inline-start",
"padding-inline-end",
"padding",
"padding-top",
"padding-right",
"padding-bottom",
"padding-left"
]
},
{
"groupName": "border",
"emptyLineBefore": "always",
"noEmptyLineBetween": true,
"properties": [
"border",
"border-block",
"border-block-color",
"border-block-style",
"border-block-width",
"border-block-start",
"border-block-start-color",
"border-block-start-style",
"border-block-start-width",
"border-inline",
"border-inline-color",
"border-inline-style",
"border-inline-width",
"border-inline-start",
"border-inline-start-color",
"border-inline-start-style",
"border-inline-start-width",
"border-top",
"border-top-color",
"border-top-style",
"border-top-width",
"border-right",
"border-right-color",
"border-right-style",
"border-right-width",
"border-bottom",
"border-bottom-color",
"border-bottom-style",
"border-bottom-width",
"border-left",
"border-left-color",
"border-left-style",
"border-left-width",
"border-collapse",
"border-color",
"border-image",
"border-image-outset",
"border-image-repeat",
"border-image-slice",
"border-image-source",
"border-image-width",
"border-radius",
"border-start-start-radius",
"border-start-end-radius",
"border-end-end-radius",
"border-end-start-radius",
"border-top-right-radius",
"border-bottom-right-radius",
"border-bottom-left-radius",
"border-top-left-radius",
"border-spacing",
"border-style",
"border-width"
]
},
{
"groupName": "outline",
"emptyLineBefore": "always",
"noEmptyLineBetween": true,
"properties": ["outline", "outline-color", "outline-offset", "outline-style", "outline-width"]
},
{
"groupName": "transform",
"emptyLineBefore": "always",
"noEmptyLineBetween": true,
"properties": ["transform", "transform-origin", "transform-style"]
},
{
"groupName": "clip-path",
"emptyLineBefore": "always",
"noEmptyLineBetween": true,
"properties": ["clip-path"]
},
{
"groupName": "font",
"emptyLineBefore": "always",
"noEmptyLineBetween": true,
"properties": [
"font",
"font-family",
"font-kerning",
"font-language-override",
"font-size",
"font-size-adjust",
"font-stretch",
"font-style",
"font-synthesis",
"font-variant",
"font-variant-alternates",
"font-variant-caps",
"font-variant-east-asian",
"font-variant-ligatures",
"font-variant-numeric",
"font-variant-position",
"font-weight"
]
},
{
"groupName": "text",
"emptyLineBefore": "always",
"noEmptyLineBetween": true,
"properties": [
"text-align",
"text-align-last",
"text-combine-upright",
"text-decoration",
"text-decoration-color",
"text-decoration-line",
"text-decoration-style",
"text-decoration-thickness",
"text-emphasis",
"text-indent",
"text-justify",
"text-orientation",
"text-overflow",
"text-shadow",
"text-transform",
"text-underline-position",
"letter-spacing",
"line-break",
"line-height",
"vertical-align",
"white-space",
"word-break",
"word-spacing",
"word-wrap",
"writing-mode"
]
},
{
"groupName": "print",
"emptyLineBefore": "always",
"noEmptyLineBetween": true,
"properties": ["page-break-after", "page-break-before", "page-break-inside"]
},
{
"groupName": "3d",
"emptyLineBefore": "always",
"noEmptyLineBetween": true,
"properties": ["perspective", "perspective-origin", "backface-visibility"]
},
{
"groupName": "transition",
"emptyLineBefore": "always",
"noEmptyLineBetween": true,
"properties": [
"transition",
"transition-delay",
"transition-duration",
"transition-property",
"transition-timing-function"
]
},
{
"groupName": "animation",
"emptyLineBefore": "always",
"noEmptyLineBetween": true,
"properties": [
"animation",
"animation-delay",
"animation-direction",
"animation-duration",
"animation-fill-mode",
"animation-iteration-count",
"animation-name",
"animation-play-state",
"animation-timing-function"
]
},
{
"groupName": "cursor & pointer",
"emptyLineBefore": "always",
"noEmptyLineBetween": true,
"properties": ["cursor", "pointer-events", "user-select"]
}
]
}
}

سعی کرده‌ایم لیست کاملی از Propertyها را در این فایل فراهم کنیم؛ اما به مرور زمان، نقص‌های احتمالی این فایل را ویرایش می‌کنیم.

فراموش نکنید مانند فایل‌های gitignore. و prettierignore. ، محتویات فایل stylelintignore. را نیز بروز کنید.

IDE Configurations

برای فعال‌کردن تنظیمات Stylelint مراحل زیر را طی کنید:

  1. از بالا-چپ بر روی File و سپس Settings کلیک کنید تا پنجرۀ تنظیمات باز شود
  2. عبارت Stylelint را در باکس بالا-چپ جست‌وجو کنید
  3. از قسمت سمت چپ بر روی گزینۀ Languages & Frameworks > Style Sheets > Stylelint کلیک کنید
  4. تیک گزینۀ Enable را فعال کنید
  5. از قسمت سمت راست عبارت مقابل Run for files را به عبارت زیر تغییر دهید
    {**/*,*}.{scss}
Scripts

برای چک‌کردن خطاهای احتمالی یا برطرف کردن آن‌ها به‌صورت خودکار، می‌توانید از دستورات زیر استفاده کنید:

npx stylelint "**/*.scss"
npx stylelint "**/*.scss" --fix

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


General

Download Size

گاهی اوقات مشاهده می‌شود که بعضی از توسعه‌دهندگان از فایل‌های حجیمی در پروژۀ خود استفاده می‌کنند که نه‌تنها مزیت خاصی ندارند، بکله باعث کند شدن سایت می‌شوند و حجم زیادی از اینترنت کاربر را اشغال می‌کنند.

پیشنهاد می‌کنیم برای کاهش حجم عکس‌ها از سایت Squoosh استفاده کنید. همچنین به طور کلی حجم فایل‌های svg از فرمت‌های دیگر کمتر است، بنابراین سعی کنید تا جای ممکن از این فرمت استفاده کنید. در صورتی که عکس شما به صورت Raster بود و امکان استفاده از svg را نداشتید، پیشنهاد ما فرمت webp می‌باشد.

به طور کلی توصیه می‌کنیم تا جای ممکن از ویدئو استفاده نکنید.

Comment

زمانی که در حال توسعۀ پروژه هستید، ممکن است بخواهید به‌طور موقت قسمتی از کد را کامنت کنید؛ این مورد به تنهایی مشکلی ندارد اما زمانی که خواستید کد را بر روی نسخۀ master یا main ببرید، حتماً کامنت‌های اضافی را پاک کنید. همچنین به طور کلی زمانی که مجبور شدید برای توضیح قسمتی از کد، از کامنت استفاده کنید، احتمالاً کدی که نوشتید تمیز نیست؛ سعی کنید همیشه کدی بنویسید که بدونِ نیاز به توضیحات، قابل‌فهم باشد.

Unicode Characters

بعضاً مشاهده می‌شود که توسعه‌دهندگان برای نمایش اعداد فارسی از کاراکترهای ۰۱۲۳۴۵۶۷۸۹ استفاده می‌کنند؛ ما پیشنهاد می‌کنیم به‌هیچ‌وجه چنین کاری نکنید؛ در عوض از فونتی استفاده کنید که کاراکترهای 0123456789 را به شکل درست نمایش دهد.


CSS

Reset

مرورگرهای مختلف، استایل‌های پیش‌فرض متفاوتی برای المان‌های مختلف دارند که این موضوع باعث بروز مشکلات متعددی در روند توسعه می‌شود؛ به‌عنوان مثال ممکن است ظاهر سایت شما در یک مرورگر خاص دقیقاً همان‌طور باشد که می‌خواهید اما در یک مرورگر دیگر کاملاً به‌هم‌ریخته باشد.

برای حل این مشکل Resetهای متعددی وجود دارد اما ما در اینجا از ساده‌ترین آن‌ها استفاده می‌کنیم:

*,
*::before,
*::after {
box-sizing: border-box;

margin: 0;
padding: 0;
}

BEM

یکی از معروف‌ترین و محبوب‌ترین متدولوژی‌های موجود که در نام‌گذاری المان‌ها استفاده می‌شود، BEM است. با استفاده از این متدولوژی می‌توانید صرفاً با نگاه‌کردن به اسم یک المان، ساختاری که در آن قرار دارد را متوجه شوید. همچنین از آن‌جایی که برای هر المان از یک کلاس مشخص استفاده می‌کنید، به مشکلات مربوط به Specificity برنخواهید خورد.

استفاده از BEM در SCSS بسیار راحت است:

.card {
&__header {
&__icon {
// ...
}

&__title {
// ...
}
}

&--primay {
// ...
}
}

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

Color Syntax

در CSS به طور کلی از 4 روش مختلف می‌توان رنگ یک المان را تعیین کرد که در ادامه به مقایسۀ آن‌ها می‌پردازیم.

Names

شاید به نظر برسد بهترین راه برای مشخص‌کردن یک رنگ، استفاده از اسم آن است. برای رنگ‌هایی که به طور معمول با آن‌ها سروکار داریم و اسم آن‌ها را می‌دانیم، این قضیه صادق است؛ اما CSS دارای بیش از 100 رنگ است که تعداد زیادی از آن‌ها برای توسعه‌دهندگان، به خصوص کسانی که زبان مادری آن‌ها انگلیسی نیست، ناآشنا هستند. از طرفی حتی در مورد رنگ‌هایی که اسم آن‌ها را می‌دانیم، نمی‌توانیم به طور دقیق میزان پُررنگ یا کم‌رنگ‌بودن آن‌ها را تشخیص دهیم. به‌عنوان مثال تفاوت رنگ‌های pink و lightpink در چیست؟ یا کدام‌یک از رنگ‌های hotpink و deeppink پُررنگ‌تر است؟

البته این نوع رنگ‌ها کاملاً هم بلااستفاده نیستند، بلکه بعضاً برای زمانی که صرفاً به‌دنبال تست‌کردن پروژه یا شناسایی باگ هستید، بهترین گزینه‌اند.

HEX

این فرمت از سه عدد در مبنای 16 تشکیل شده است؛ آیا برای اثبات نابهینه‌بودن این فرمت احتیاج به توضیح بیشتری هست؟ جدای از آنکه تشخیص یک عدد مبنای 16 و محاسبه ذهنی برای تبدیل آن به مبنای 10 کاری دشوار است، این فرمت دارای مشکل بزرگ دیگری است که در قسمت RGB به آن اشاره می‌کنیم.

RGB

این فرمت از سه عدد در مبنای 10 تشکیل شده است که به ترتیب نشان‌دهندۀ رنگ‌های قرمز، سبز و آبی هستند. این اعداد از 0 تا 255 می‌توانند متغیر باشند و هر چه عدد متناظر یک رنگ بزرگ‌تر باشد، مقدار بیشتری از آن در رنگ نهایی استفاده خواهد شد.

مشکل این فرمت آن است که نمی‌توان رنگ فعلی را به راحتی تشخیص داد. به‌عنوان مثال rgb(48, 15, 16) مربوط به چه رنگی است؟ درست است که می‌توان متوجه شد رنگ قرمز تقریباً 3 برابر رنگ‌های سبز و آبی استفاده شده است، اما اینکه رنگ نهایی چه چیزی است، بسیار دشوار است. همچنین ساخت نسخه‌های روشن‌تر یا تیره‌تر با استفاده از این فرمت به هیچ عنوان ساده نیست.

HSL

این فرمت از سه قسمت تشکیل شده است که به ترتیب یک زاویه در Color Wheel، درصد استفاده از رنگ پایه و درصد روشنایی رنگ نهایی را مشخص می‌کنند. اگر Color Wheel و زاویۀ رنگ‌های اصلی را حفظ کنید، به راحتی می‌توانید رنگی که با این فرمت ساخته می‌شود را تشخیص دهید. حفظ‌کردن Color Wheel بسیار ساده‌تر از چیزی است که فکر می‌کنید؛ صرفاً کافی است رنگ‌های رنگین‌کمان را بلد باشد:

  • قرمز: 0
  • نارنجی: 30
  • زرد: 60
  • سبز: 120
  • آبی: 180
  • نیلی: 240
  • بنفش: 270

ایجاد قالب‌های رنگی با استفاده از این فرمت بسیار ساده است؛ از قسمت اول برای تغییر رنگ پایه می‌توانید استفاده کنید و صرفاً با افزودن 180 درجه، به رنگ مکمل می‌رسید؛ قسمت دوم برای تعیین میزان روشنایی در قالب‌های Light و Dark کاربرد دارد؛ قسمت سوم مربوط به روشنایی رنگ نهایی است و می‌توانید از آن برای ایجاد رنگ Stateهایی مانند Hover استفاده کنید.

:root {
--primary-hue: 220;
--secondary-hue: 260;
--success-hue: 120;
--warning-hue: 20;
--danger-hue: 0;

--color-gray-98: hsl(0, 0%, 98%);
--color-gray-90: hsl(0, 0%, 90%);
--color-gray-80: hsl(0, 0%, 80%);
--color-gray-70: hsl(0, 0%, 70%);
--color-gray-40: hsl(0, 0%, 40%);
--color-gray-30: hsl(0, 0%, 30%);
--color-gray-20: hsl(0, 0%, 20%);
--color-gray-10: hsl(0, 0%, 10%);

--color--default-background: var(--color-gray-98);
--color--default-foreground: var(--color-gray-10);

--color--primary: hsl(var(--primary-hue), 100%, 56%);
--color--primary-fade: hsl(var(--primary-hue), 100%, 96%);
--color--primary-lighter: hsl(var(--primary-hue), 100%, 60%);
--color--primary-darker: hsl(var(--primary-hue), 100%, 52%);
--color--primary-opposite: var(--color-gray-98);

--color--secondary: hsl(var(--secondary-hue), 50%, 50%);
--color--secondary-fade: hsl(var(--secondary-hue), 100%, 90%);
--color--secondary-lighter: hsl(var(--secondary-hue), 50%, 56%);
--color--secondary-darker: hsl(var(--secondary-hue), 50%, 44%);
--color--secondary-opposite: var(--color-gray-98);

--color--warning: hsl(var(--warning-hue), 100%, 44%);
--color--warning-fade: hsl(var(--warning-hue), 100%, 90%);
--color--warning-lighter: hsl(var(--warning-hue), 100%, 50%);
--color--warning-darker: hsl(var(--warning-hue), 100%, 40%);
--color--warning-opposite: var(--color-gray-98);

--color--success: hsl(var(--success-hue), 100%, 40%);
--color--success-fade: hsl(var(--success-hue), 100%, 92%);
--color--success-lighter: hsl(var(--success-hue), 100%, 44%);
--color--success-darker: hsl(var(--success-hue), 100%, 36%);
--color--success-opposite: var(--color-gray-98);

--color--danger: hsl(var(--danger-hue), 100%, 36%);
--color--danger-fade: hsl(var(--danger-hue), 100%, 90%);
--color--danger-lighter: hsl(var(--danger-hue), 100%, 42%);
--color--danger-darker: hsl(var(--danger-hue), 100%, 30%);
--color--danger-opposite: var(--color-gray-98);
}

Font Management

Multiple Fonts

در کل پروژه فقط از یک فونت استفاده کنید. کمتر پیش می‌آید استفاده از دو فونت مجزا، نتیجۀ مطلوبی داشته باشد؛ مخصوصاً برای سایت‌های فارسی. اگر قسمتی از سایت شما فارسی و قسمت دیگر انگلیسی است، از دو فونت برای آن‌ها استفاده نکنید؛ بلکه به‌دنبال فونتی بگردید که هر دو را به خوبی نشان دهد.

ما فونت‌های فارسی رایگان زیر را پیشنهاد می‌کنیم:

font-family

استایل‌های مربوط به font-family را به * ندهید چراکه باعث بالا رفتن Specificity می‌شود و در کار بعضی از کتابخانه‌ها مانند Fontawesome یا Monaco Editor خلل ایجاد می‌کند. در عوض می‌توانید این استایل را به html بدهید. از آنجایی که font از المان پدر به ارث می‌رسد، تمام المان‌های شما فونت مورد نظر را خواهند داشت. اما بعضی از المان‌ها مانند input و button احتیاج دارند که به طور دقیق فونت آن‌ها را مشخص کنید.

برای راحتی کار می‌توانید از کد زیر استفاده کنید:

html {
font-family: Poppins, sans-serif;
}

input,
label,
select,
textarea,
button,
fieldset,
legend,
datalist,
output,
option,
optgroup {
font-family: inherit;
}
Generic Default Fonts

همیشه یک فونت پیش‌فرض مانند sans-serif را به آخر font-family اضافه کنید تا در صورتی که به هر دلیلی فونت اصلی از کار افتاد، مرورگر بتواند از فونت پیش‌فرض استفاده کند. دقت کنید که فقط فونت‌های Generic می‌توانند به‌عنوان Fallback استفاده شوند؛ یعنی:

  • serif
  • sans-serif
  • cursive
  • fantasy
  • monospace

rem & px

همان‌طور که می‌دانید به طور پیش‌فرض هر rem برابر با 16px است. این موضوع باعث می‌شود زمانی که می‌خواهید از rem استفاده کنید، مجبور به انجام محاسبات نسبتاً سختی باشید که سرعت توسعه و از همه مهم‌تر خوانایی کد را کاهش می‌دهد. برای حل این مشکل پیشنهاد می‌کنیم سایز فونت html را به %62.5 تغییر دهید؛ با این کار هر rem برابر با 10px خواهد شد.

Variables

ما پیشنهاد می‌کنیم همیشه به طور پیش‌فرض از متغیرهای CSS استفاده کنید و تنها در صورتِ نیاز به متغیرهای SCSS رجوع کنید.

همچنین پیشنهاد می‌کنیم متغیرها را قبل از بقیۀ Ruleها تعریف کنید تا خوانایی کد بالا رود و از اشتباهات احتمالی جلوگیری شود:

.item {
--size: 10rem;
--gap: 1rem;

/* ... */
}

Transition/Animation Performance

در صورتی که فقط از transform و opacity برای انجام Animationهای خود استفاده کنید، فقط مرحله Composite اجرا می‌شود و مرورگر می‌تواند تمام کارها را بر عهدۀ GPU بگذارد. بنابراین همیشه سعی کنید فقط از این دو Property استفاده کنید.

دقت کنید که در اینجا منظور تنها کلیدواژۀ animation یا keyframe نیست، بلکه transition و به طور کلی هر راهی که بتوان با آن ظاهر یک المان را به تدریج عوض کرد، مد نظر است.

Zero Unit

در CSS زمانی که از عدد 0 استفاده می‌کنید، بهتر است واحدی برای آن در نظر نگیرید.

/* Bad */
.item {
margin: 0rem auto;
}

/* Good */
.item {
margin: 0 auto;
}

Logical Properties

اگر قبلاً بر روی پروژه‌ای کار کرده باشید که هم‌زمان باید از دو زبان انگلیسی و فارسی پشتیبانی می‌کرد، قطعاً متوجه شده‌اید که نیاز است خیلی از استایل‌هایی که به راست یا چپ ربط دارند را عوض کنید. به‌عنوان مثال اگر از margin-left در نسخۀ انگلیسی استفاده کردید، باید در نسخۀ فارسی آن را به margin-right تغییر می‌دادید. خوشبختانه در CSS برای حل این مشکل می‌توان از Logical Properties استفاده کنیم؛ در این صورت، مرورگر بر اساس Direction سایت، استایل‌ها را اعمال می‌کند.

در ادامه برای سایتی که LTR است، معادل چند Property پُر استفاده را می‌آوریم:

Normal PropertyLogical Property
topinset-block-start
bottominset-block-end
leftinset-inline-start
rightinset-inline-end
margin-topmargin-block-start
margin-bottommargin-block-end
margin-leftmargin-inline-start
margin-rightmargin-inline-end
padding-toppadding-block-start
padding-bottompadding-block-end
padding-leftpadding-inline-start
padding-rightpadding-inline-end
heightblock-size
min-heightmin-block-size
max-heightmax-block-size
widthinline-size
min-widthmin-inline-size
max-widthmax-inline-size

هم‌چنین می‌توانید دو Logical Property را که هر دو Inline یا Block هستند، همزمان مقداردهی کنید. برای روشن‌تر شدن موضوع، سه کد معادل زیر را در نظر بگیرید:

.item {
padding-top: 1rem;
padding-bottom: 1rem;
}

/* or */

.item {
padding-block-start: 1rem;
padding-block-end: 1rem;
}

/* or */

.item {
padding-block: 1rem;
}

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


Comment Dictionary

برخی از اشتباهات پُرتکرار هستند و کامنتی که برای رفع آن‌ها گذاشته می‌شود همیشه یکسان است. برای این موارد، از علائم و اختصارات استفاده می‌کنیم که سرعت Review بیشتر شود.

کامنتمعنی
xپاک شود
^بالای این خط، یک خط خالی اضافه شود
vپایین این خط، یک خط خالی اضافه شود
reeخط خالی اضافه پاک شود
نمیگممشکلی که وجود دارد، قبلاً ذکر شده بود؛ برای جلوگیری از تعدد کامنت‌ها، این موضوع دیگر تذکر داده نمی‌شود، اما باید در تمام نقاط پروژه مورد بررسی قرار گیرد و حل شود
گفتمبه این مشکل در مستند Clean Code اشاره شده؛ بنابراین انتظار دارم نیازی به تذکر دوباره نباشد
چسبدر HTML، زمانی که محتوای یک المان خالی باشد، باید Closing Tag را به Opening Tag بچسبانید و هیچ فاصله‌ای، حتی Enter یا Space، بین آن‌ها نباشد
unitدر CSS، از Unit مناسبی استفاده نشده. در اکثر مواقع باید از rem استفاده کنید
nestاز قابلیت Nesting در SCSS استفاده کنید و ساختاری که در HTML وجود دارد را در CSS هم بیاورید
short itدر CSS به جای آوردن چند دستور در چند خط، از فرمت Shorthand آن‌ها استفاده کنید
tall itاز Shorthand استفاده نکنید و دستورات را در چند خط جدا بیاورید
غغیرقابل‌قبول
😐 یا :/تَهِ اعتراض!

پروژه

از این پس، برای تمام PRهایی که می‌زنید، از هر دو عضوِ یکی از تیم‌ها درخواست کنید تا کد شما را جداگانه Review کنند؛ بعد از اینکه Approve کردند، به منتور عادی مراجعه کنید و پس از Approve او، به منتور سنیور بگویید تا کد شما را ببیند.

همچنین لازم است شما هم حداقل کد یک تیم دیگر را Review کنید. سعی کنید تمام مواردی که در این فاز یاد گرفتید را هنگام Review مورد بررسی قرار دهید.

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