Tailwind CSS یک فریمورک utility-first است که به ما این امکان را میدهد تا بتوانیم وبسایتهای مدرن را به سرعت و بدون ترک فایل HTML خود بسازیم. این فریمورک از زمان انتشارش در سال ۲۰۱۹ تا به امروز، محبوبیت قابل توجهی را به دست آورده است.
امروزه اگر به دنبال «برترین فریمورکهای CSS» باشیم، احتمالاً Tailwind CSS را در کنار نامهای بزرگی مانند Bootstrap و Bulma خواهیم یافت.
در این مقاله، تغییرات نسخه ۴ از Tailwind CSS را بررسی میکنیم، سپس استراتژیهای انتقال پروژههای موجود به این نسخه را توضیح میدهیم. همچنین، با ارائه مثالهایی، ویژگیهای جدید آن را معرفی کرده و در نهایت، این نسخه را با سایر فریمورکهای CSS مقایسه میکنیم تا مزایا و محدودیتهای آن را بهتر بشناسیم.
تیم Tailwind CSS در مارس ۲۰۲۴ از موتور عملکردی جدید خود با نام Tailwind Oxide رونمایی کرد. این موتور مزایای متعددی دارد، از جمله زنجیره ابزار یکپارچه و پیکربندی سادهتر برای سرعت بخشیدن به فرایند build.
در نسخه ۳ Tailwind CSS، فایل tailwind.config.js
به ما این امکان را میدهد که توکنهای طراحی پیشفرض را تغییر دهیم. این فایل در واقع مرکز شخصیسازی است که میتوانیم در آن:
یکی از مهمترین وظایف این فایل، مشخصکردن منابع محتوایی پروژه است تا tailwind بتواند کلاسهای مورد نیاز را اسکن و خروجی صحیح تولید کند.
در نسخه ۳ Tailwind CSS، هنگام راهاندازی یک پروژه جدید، کد پیشفرض tailwind.config.js
به این شکل بود:
/** @type {import('tailwindcss').Config} */ export default { content: [ "./index.html", "./src/**/*.{js,ts,jsx,tsx}", ], theme: { extend: {}, }, plugins: [], }
بعد از پیکربندی فایل config، گام بعدی اضافهکردن دستورهای tailwind به فایل index.css
است.
در نسخه ۳ Tailwind CSS، این دستورها شامل موارد زیر بودند:
@tailwind base; @tailwind components; @tailwind utilities;
اما در نسخه ۴ Tailwind CSS، دیگر نیازی به فایل tailwind.config.js
و دستورهای @tailwind
نخواهیم داشت.
فقط کافی است "tailwindcss"
را در فایل CSS اصلی خود import کنیم:
@import "tailwindcss";
با انجام این کار، مراحل راهاندازی یک پروژه جدید کمتر شده و تعداد فایلهای مورد نیاز نیز کاهش پیدا میکند.
با این حال، اگر یک پروژه قدیمی با نسخه ۳ داریم و همچنان به فایل JS برای پیکربندی نیاز داریم، میتوانیم از دستور جدید @config
در فایل CSS استفاده کنید تا تنظیمات قبلی را بارگذاری نماییم:
@import "tailwindcss"; @config "../../tailwind.config.js";
البته، همه قابلیتهای نسخههای قبلی در نسخه ۴ نهایی پشتیبانی نخواهند شد. برای مثال، ویژگیهایی مثل corePlugins
، important
و separator
ممکن است حذف شوند. همچنین، برخی گزینهها مانند safelist
نیز احتمالاً با تغییراتی در نحوه عملکردشان مجددا مورد استفاده قرار بگیرند.
اگر فایلهایی وجود دارند که نمیخواهیم آنها را شامل شوند، میتوانیم هنگام import کردن tailwind از تابع source()
استفاده کنیم تا تشخیص خودکار را محدود نماییم:
@import "tailwindcss" source("../src");
برای فایلهایی که tailwind به صورت پیشفرض آنها را شناسایی نمیکند، مثل فایلهای داخل .gitignore
، میتوانیم از دستور @source
استفاده کنیم:
@import "tailwindcss"; @source "../node_modules/@my-company/ui-lib/src/components";
همچنین، اگر بخواهیم تشخیص منبع را کاملاً غیرفعال کنیم، میتوانیم این کار را انجام دهیم:
@import "tailwindcss" source(none);
اگر نمیخواهیم استایلهای پیشفرض Tailwind را استفاده کنیم، میتوانیم لایههای مورد نیاز را مستقیماً import کرده و بقیه را حذف نماییم:
@layer theme, base, components, utilities; @import "tailwindcss/theme" layer(theme); @import "tailwindcss/utilities" layer(utilities);
این کار به ما کمک میکند تا فقط استایلهای ضروری را در پروژه خود داشته باشیم.
رویکرد جدید CSS-first در نسخه ۴ Tailwind CSS، شخصیسازی استایلها را بسیار سادهتر کرده است. در این نسخه، تمام سفارشیسازیها را به جای این که در فایل پیکربندی جاوااسکریپت انجام دهیم، مستقیماً در فایل اصلی CSS اعمال میکنیم.
در نسخه ۳، اگر بخواهیم رنگهای جدید را به یک تم سفارشی اضافه کنیم، باید آنها را در بخش theme
فایل tailwind.config.js
تعریف نماییم:
/** @type {import('tailwindcss').Config} */ export default { content: [ "./index.html", "./src/**/*.{js,ts,jsx,tsx}", ], theme: { extend: { colors: { background:'#764abc', lilac: '#eabad2', light: '#eae3f5' } }, }, plugins: [], }
و سپس این کلاسهای utility را در HTML استفاده کنیم:
<div className="bg-background"> <header className="flex justify-between py-4 px-8"> <a href="/" className="text-light">LogRocket - Oscar</a> <ul className="text-lilac"> <li><a href="#">Home</a></li> <li><a href="#">About</a></li> <li><a href="#">Contact</a></li> </ul> </header>
در این مثال، کلاسهای bg-background
، text-light
و text-lilac
به المنتهای HTML اضافه شدهاند.
در نسخه ۴، تمام شخصیسازیها را با دستور @theme
در CSS انجام میدهیم:
@import "tailwindcss"; @theme { --color-background-100: #764abc; --color-lilac-100: #eabad2; --color-light-100: #eae3f5; }
و سپس مانند همیشه، این کلاسها را در HTML مورد استفاده قرار میدهیم:
<div className="bg-background-100"> <header className="flex justify-between py-4 px-8"> <a href="/" className="text-light-100">LogRocket - Oscar</a> <ul className="text-lilac-100"> <li><a href="#">Home</a></li> <li><a href="#">About</a></li> <li><a href="#">Contact</a></li> </ul> </header>
نکتهای که باید به آن توجه داشته باشیم این است که اگر از VS Code استفاده میکنیم، ممکن است دستور @import
به عنوان خطا نمایش داده شود، اما جای نگرانی وجود ندارد، و همه چیز به درستی کار خواهد کرد.
همچنین، مثالهای بالا در React و Tailwind CSS ایجاد شدهاند، به همین دلیل بهجای class
از className
در آنها استفاده کردهایم. اما فارغ از فریمورک، کلاسهای utility همیشه یکسان خواهند بود.
همانطور که در مثال قبلی دیدیم، در نسخه ۴، متغیرهای CSS تمام استایلهای مربوط به تم را کنترل میکنند:
@theme { --font-display: "Poppins", "sans-serif"; --ease-fluid: cubic-bezier(0.3,0,0,1); --color-background-100: #764abc; }
در نسخه جدید، میتوانیم یک namespace خاص از تم را بازنویسی کنیم، مانند رنگها، فونتها، متن و غیره؛ یا حتی کل تم Tailwind را سفارشیسازی نماییم. این کار به راحتی در فایل اصلی CSS انجام میشود.
اگر بخواهیم کل تم پیشفرض را بازنویسی کنیم، میتوانیم از --*: initial
استفاده نماییم. مثلاً برای بازنویسی فونت پیشفرض و تعریف فونت سفارشی خودمان میتوانیم به روش زیر پیش برویم:
@import "tailwindcss"; @theme { --font-*: initial --font-display: "Poppins", "sans-serif"; }
در این حالت، font-display
تنها font-family
خواهد بود که در پروژه در دسترس است.
همچنین میتوانیم برای هر ویژگی یک مقدار پیشفرض با استفاده از double-dashها تعیین کنیم. مثلاً برای تنظیم فونت و استایل متن در Tailwind CSS، میتوانیم کد زیر را داشته باشیم:
@import "tailwindcss"; @import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap'); @theme { --font-display : "Poppins", sans-serif; --font-logo: "Roboto", sans-serif; --text-logo: 1.5rem; --text-logo--font-weight: 700; --text-big: 6rem; --text-big--font-weight: 700; --text-big--letter-spacing: -0.025em; --color-background-100: #764abc; --color-lilac-100: #eabad2; --color-light-100: #eae3f5; }
در این مثال، دو فونت مختلف را تعریف کردهایم:
--font-display
برای متنهای معمولی--font-logo
برای لوگوهمچنین چندین کلاس جدید برای size و weight متن نیز اضافه کردهایم.
بنابراین، وقتی کلاس utility مانند text-logo
را در HTML خود اضافه کنیم، آن المنت به طور پیشفرض دارای font-size
برابر با ۱٫۵rem
و font-weight
برابر با ۷۰۰
خواهد بود. به طور مشابه، هر المنت با نام کلاس text-big
، به طور پیشفرض دارای font-size
برابر با ۶rem
، و font-weight
برابر با ۷۰۰
و letter-spacing
برابر با -۰٫۰۲۵em
خواهد بود.
اکنون میتوانیم از این کلاسها در کد HTML خود استفاده کنیم:
<div className="bg-background-100 h-screen"> <header className="flex justify-between py-4 px-8"> <a href="/" className="font-logo text-logo text-light-100">LogRocket - Oscar</a> <ul className="hidden md:flex flex items-center align-middle gap-4 font-display text-lilac-100"> <li> <a href="#" className="py-2 px-4 rounded-md">Home</a> </li> <li><a href="#" className="">About</a></li> <li><a href="#" className="">Contact</a></li> </ul> </header> <div className="container px-32 py-32 font-display"> <div className="flex"> <div> <h1 className="text-lilac-100 text-big">Tailwind CSS</h1> <br /> <h3 className="text-3xl text-light-100"> Build websites with utility classes from the comfort of your HTML </h3> <br /> <p className="text-2xl text-light"> Lorem ipsum dolor sit amet consectetur adipisicing elit. Fu gi at veniet atque unde laudantium. Ipsa nam quisquam quod non fficiis porro? Lorem ipsum dolor, sit amet consectetur adipisicing elit. Eos iure nemo a hic sunt incidunt? </p> </div> </div> </div> </div>
در نسخه ۴ Tailwind CSS، وابستگی به مقادیر پیشفرض کاهش یافته و میتوانیم چندین کلاس را با یک کلاس سفارشی جایگزین نماییم. برای مثال، در اینجا کلاس text-big
جایگزین text-5xl
و text-bold
برای کلاسهای مربوط به هدر h1
شده است.
همچنین میتوانیم این روش را برای تمام utilityهای دیگر در Tailwind نیز به کار ببریم.
برخی از utilityها، دیگر وابسته به تنظیمات تم در نسخه ۴ Tailwind CSS نیستند. حال میتوانیم مستقیماً در فایل HTML خود، بدون پیکربندی اضافی، مقدار دلخواهمان را مشخص کنیم.
در نسخه ۳ Tailwind CSS، برای تعیین تعداد ستونها باید مقدار آن را در فایل tailwind.config.js
تعریف میکردیم، اما در نسخه ۴ میتوانیم از هر عددی بین grid-cols-5
تا grid-cols-73
استفاده کنیم. این موضوع برای utilityهای z-index (مانند z-40
) و opacity-*
نیز صدق میکند.
نسخه ۴ Tailwind CSS همچنین از variantها مثل data-*
به صورت پیشفرض پشتیبانی میکند و نیازی به arbitrary valueها نخواهیم داشت.
مزیت اصلی این تغییرات این است که توسعهدهندگان زمان کمتری را برای پیکربندی توکنهای طراحی غیرضروری یا غیراصلی صرف میکنند.
Utilityهای فاصلهگذاری مثل m-*
, w-*
, mt-*
, px-*
و غیره، در نسخه ۴ Tailwind CSS به صورت داینامیک و بر اساس مقدار پایه ۰٫۲۵rem
که در تم پیشفرض تعریف شده است، ایجاد میشوند.
هر مقدار در این مقیاس، ضریبی از مقدار پایه خواهد بود. بهعنوان مثال:
mt-1
برابر با ۰٫۲۵rem
است.mt-2
برابر با ۰٫۵rem
(۰٫۲۵rem
* 2
) است.mt-21
برابر با ۵٫۲۵rem
خواهد بود.قابلیت جدیدی که در نسخه ۴ اضافه شده است این است که میتوانیم از مقادیر فاصلهگذاری که به طور صریح تعریف نشدهاند، استفاده کنیم. در نسخه ۳ Tailwind CSS، مجبور بودیم مقادیری مانند mt-[5.25rem]
را به صورت arbitrary value بنویسیم یا یک تم سفارشی ایجاد کنیم. اما در نسخه ۴، دیگر نیازی به این کار نیست و میتوانیم طراحیهای منسجمتری ایجاد نماییم.
اگر بخواهیم مقادیر پیشفرض فاصلهگذاری را غیرفعال کرده و مقیاس سفارشی خودمان را تعریف کنیم، میتوانیم به این صورت عمل کنیم:
@theme { --spacing: initial --spacing-1: 0.25rem --spacing-2: 0.5rem --spacing-4: 1rem --spacing-8: 2rem --spacing-12: 3rem }
با این تنظیمات، تمامی utilityهای فاصلهگذاری در Tailwind CSS فقط از مقادیر مشخص شده استفاده خواهند کرد.
نسخه ۴ Tailwind CSS به جای استفاده از پالت رنگ rgb
، از oklch
استفاده میکند. این تغییر باعث میشود تا رنگها زندهتر و پویاتر باشند و دیگر محدودیتهای rgb
را نداشته باشیم.
نسخه ۴ Tailwind CSS، از Container Queryها به صورت پیشفرض پشتیبانی میکند و دیگر نیازی به استفاده از پلاگین @tailwindcss/container-queries
برای ایجاد Containerهای ریسپانسیو نداریم.
Container Query چیست؟
این ویژگی به ما این امکان را میدهد که استایل یک المان را بر اساس اندازه parent آن تنظیم کنیم، نه کل viewport. به این ترتیب، چیدمان سایت ما بر اساس اندازه هر کامپوننت تطبیق پیدا میکند.
برای ایجاد Container Queryها در نسخه ۴ Tailwind CSS، کافی است کلاس @container
را به المنت parent اضافه کنیم. سپس برای المنتهای child، میتوانیم ازutilityهای ریسپانسیو مانند @sm
و @lg
استفاده کنیم تا استایلها را بر اساس اندازه parent تنظیم نماییم:
<div className="@container"> <header className="flex justify-between @sm:grid-cols-2 @lg:grid-cols-4"> <!-- child content --> </header> </div>
در نسخه ۴ Tailwind CSS، یک variant جدید به نام @max-*
اضافه شده که به ما اجازه میدهد استایلها را، زمانی که عرض container کمتر از مقدار مشخصی شد، تغییر دهیم. میتوانیم @min-*
و @max-*
را ترکیب کنیم تا یک بازه خاص را تعریف نماییم:
<div className="@container"> <div className="flex @min-md:@max-xl:hidden"> <!-- child content --> </div> </div>
در این کد، div
داخلی زمانی مخفی خواهد شد که عرض container بین md
و xl
(یعنی بین ۷۶۸px
و ۱۲۸۰px
) باشد.
این قابلیت انعطافپذیری بیشتری ایجاد میکند و با اکثر مرورگرها سازگار است، بنابراین میتوانیم از همین حالا در پروژههای نسخه ۴ Tailwind CSS خود از آن استفاده کنیم.
اگر قصد داریم پروژهای که از نسخه ۳ استفاده میکند را به نسخه ۴ ارتقا دهیم، Tailwind CSS یک ابزار مخصوص برای این کار ارائه کرده است که بیشتر مراحل را به صورت خودکار انجام میدهد.
برای ارتقای پروژه، دستور زیر را اجرا میکنیم:
npx @tailwindcss/upgrade@next
این ابزار وظایفی مانند بهروزرسانی وابستگیها، انتقال فایل پیکربندی جاوااسکریپت به CSS و مدیریت تغییرات در فایلهای قالب را به طور خودکار انجام میدهد.
git diff
برای بررسی تغییرات استفاده کنیم.ممکن است در پروژههای پیچیده نیاز به انجام تغییرات دستی داشته باشیم. Tailwind CSS فهرستی از تغییرات کلیدی و نحوه سازگاری با آنها را ارائه کرده است که آنها در ادامه بررسی میکنیم.
افزونه PostCSS:
در نسخه ۴، افزونه PostCSS اکنون در قالب یک پکیج اختصاصی با نام @tailwindcss/postcss
ارائه شده است. میتوانیم postcss-import
و auto-prefixer
را از فایل postcss.config.mjs
پروژه خود حذف نماییم:
export default { plugins: { '@tailwindcss/postcss': {}, }, };
اگر قصد داریم پروژه جدیدی راهاندازی کنیم، میتوانیم Tailwind را همراه با افزونه PostCSS به صورت زیر نصب نماییم:
npm install tailwindcss@next @tailwindcss/postcss@next
افزونه Vite:
در نسخه ۴ Tailwind CSS، یک افزونه اختصاصی برای Vite ارائه شده است که توصیه میشود از آن به جای افزونه PostCSS استفاده کنیم:
import { defineConfig } from 'vite'; import tailwindcss from '@tailwindcss/vite'; export default defineConfig({ plugins: [ tailwindcss() ], });
برای نصب Tailwind همراه با این افزونه در پروژه جدید، میتوانیم از دستور زیر استفاده کنیم:
npm install tailwindcss@next @tailwindcss/vite@next
Tailwind CLI:
ابزار CLI اکنون در یک پکیج اختصاصی با نام @tailwind/cli
ارائه شده است. بنابراین، باید دستورات build را مطابق زیر تغییر دهیم:
npx @tailwindcss/cli -i input.css -o output.css
برخی از کلاسهای قدیمی در نسخه ۴ دیگر پشتیبانی نمیشوند و باید از جایگزینهای جدید آنها استفاده کنیم، که عبارتند از:
bg-opacity-*
: به جای آن باید از اصلاحکنندههای شفافیت مانند bg-black/50
استفاده کنیم.text-opacity-*
: به جای آن باید از text-black/50
استفاده کنیم.border-opacity-*
: به جای آن باید از border-black/50
استفاده کنیم.divide-opacity-*
: به جای آن باید از divide-black/50
استفاده کنیم.ring-opacity-*
: به جای آن باید از ring-black/50
استفاده کنیم.placeholder-opacity-*
: به جای آن باید از placeholder-black/50
استفاده کنیم.flex-shrink-*
: اکنون باید از shrink-*
استفاده کنیم.flex-grow-*
: جایگزین آن grow-*
است.overflow-ellipsis
: اکنون باید از text-ellipsis
استفاده کنیم.decoration-slice
: به جای آن باید از box-decoration-slice
استفاده کنیم.decoration-clone
: جایگزین آن box-decoration-clone
است.در نسخه ۴، utility container
باید با @utility
پیکربندی شود:
@import "tailwindcss"; @utility container { margin-inline: auto; padding-inline: 2rem; }
گزینههای پیکربندی مانند center
و padding
دیگر در نسخه ۴ وجود ندارند.
در این نسخه، مقادیر پیشفرض برای utilityهای shadow
، blur
و border-radius
تنظیم شدهاند تا نامگذاری واضحتری داشته باشند. بنابراین، باید هر یک از این برای utilityها را در پروژه خود جایگزین کنیم تا ظاهر پروژه تغییر نکند.
در نسخه ۳ Tailwind CSS، رنگ پیشفرض border مقدار gray-200
بود و نیازی به تنظیم دستی آن نداشتیم:
<header className="flex justify-between border-b-2 py-4 px-8"> <--! content --> </header>
اما در نسخه ۴، رنگ border مقدار currentColor
را دریافت میکند و در صورت مشخص نکردن رنگ، ممکن است تغییرات ظاهری ایجاد شود.
اگر میخواهیم رفتار نسخه ۳ را حفظ نماییم، باید این کد را به پروژه خود اضافه کنیم:
the v3 behavior: @import "tailwindcss"; @layer base { *, ::after, ::before, ::backdrop, ::file-selector-button { border-color: var(--color-gray-200, currentColor); } }
در نسخه ۳، utility ring
مقدار ۳px
داشت، اما در نسخه ۴ مقدار آن به ۱px
کاهش یافته است. برای حفظ ظاهر پروژه، باید ring-3
را جایگزین ring
کنیم.
در نسخه ۴ Tailwind CSS، متن placeholder به طور پیشفرض رنگ متن جاری را با ۵۰٪ شفافیت دریافت میکند. در نسخه ۳، این مقدار gray-400
بود. برای حفظ این رفتار، میتوانیم این کد را به CSS پروژه خود اضافه کنیم:
@layer base { input::placeholder, textarea::placeholder { color: theme(--color-gray-400); } }
در نسخه ۴، utility outline-none
دیگر یک outline شفاف با ضخامت ۲px
اضافه نمیکند، در حالی که در نسخه ۳ این کار را انجام میداد. به جای آن، utility جدید outline-hidden
معرفی شده است که مانند outline-none
در نسخه ۳ عمل میکند.
برای حفظ ظاهر پروژه هنگام بهروزرسانی، باید outline-none
را با outline-hidden
جایگزین کنیم، مگر اینکه بخواهیم outline را کاملاً حذف نماییم.
در نسخه ۴، utilityهای سفارشی دیگر با utility @layer
تعریف نمیشوند و باید از @utility
API استفاده کنیم که سازگاری بیشتری با cascade layers دارد:
@utility tab-4 { tab-size: 4; }
در نسخه ۴، variantهایی مانند first
و last
به ترتیب از چپ به راست قرار میگیرند. بنابراین، هنگام بهروزرسانی پروژه، باید ترتیب variantها را در کد خود تنظیم کنیم.
در این نسخه، برای مقداردهی متغیرهای CSS در مقدارهای دلخواه، از ()
به جای []
استفاده میشود. برای مثال:
<div class="bg-(--brand-color)"> <!-- ... --> </div>
ما باید این مورد را نیز در پروژه خود بهروزرسانی نماییم.
در نسخه ۴ Tailwind CSS، استایلهای hover فقط در دستگاههایی که از تعامل hover پشتیبانی میکنند، اعمال میشود. برای حفظ سازگاری، میتوانیم hover
را به صورت دستی در فایل CSS تعریف کنیم:
@import "tailwindcss"; @variant hover (&:hover);
theme()
در نسخه ۴، Tailwind CSS برای همه مقادیر theme متغیرهای CSS تولید میکند، بنابراین دیگر نیازی به استفاده از theme()
نیست. توصیه میشود در پروژه خود، theme()
را با متغیرهای CSS جایگزین نماییم:
@import "tailwindcss"; .my-class { background-color: var(--color-red-500); }
یکی از جایگزینهای محبوب Tailwind CSS، فریمورک Bootstrap است که مجموعهای گسترده از کامپوننتهای آماده را ارائه میدهد.
با وجود محبوبیت زیاد، اما Tailwind CSS همچنان دارای محدودیتهایی نیز میباشد:
بهروزرسانی پروژههای موجود به نسخه ۴ Tailwind CSS ممکن است در ابتدا کمی دشوار به نظر برسد، مخصوصاً اگر پروژه پیچیدهای داشته باشیم، اما مزایای آن ارزش این تغییر را دارد. این نسخه با حذف utilityهای اضافی و ارائهی سینتکس شفافتر، کار با Tailwind CSS را سادهتر و سریعتر میکند.
دیدگاهها:
متین گرشاسبی
بهمن 22, 1403 در 8:29 ق.ظ
سلام استاد خا قوت، ممنون بابت این پست
ولی بنده هنوز مشکل دارم در اضافه کردن یک فونت جدید! میشه راهنمایی بفرمایید چطوری از فونت جدید اضافه کنی؟ (فایل فونت رو دارم)
مسعود صدری
بهمن 22, 1403 در 10:41 ب.ظ
سلام
ممنونم.
لطفا تلگرام پیام بدید تا بررسی کنیم.
khadem13359
بهمن 19, 1403 در 8:36 ق.ظ
توضیحات و مثال ها بسیار عالی بودند و کاملا واضح و قابل درک
خیلی متشکر ♥