الوحدة 4 · إطلاق مشروع حقيقي
التنسيق باستخدام Tailwind
17 دقراءة 4 داملأ الفراغات
الدرس في لمحة
أضف Tailwind CSS v4 إلى مشروع Vite عبر الـ plugin الرسمي، ونسّق Watchlist بالـ utility classes ومتغيّرات الحالة (variants)، وخصّص السمة داخل CSS.
لا فيديو هنا، بل الشيء الحقيقي: درس مكتوب وأمثلة قابلة للتشغيل واختبار وتدريب.
Watchlist يعمل، لكنه يبدو كأنه من عام 1998. يمكنك كتابة stylesheet، وكثير من التطبيقات الممتازة تفعل ذلك. لكن هذه الدورة تستخدم Tailwind CSS، لأنه شائع جدًا في مشاريع React التي ستنضمّ إليها، ولأن التنسيق داخل الـ JSX مباشرة يُبقي الـ components مكتفية بذاتها: ملف MovieItem يحمل الـ markup والسلوك والمظهر معًا.
Tailwind مجموعة كبيرة من الـ utility classes الصغيرة، كلٌّ منها تفعل شيئًا واحدًا: p-4 تضيف حشوًا، وrounded-lg تدوّر الزوايا، وtext-slate-600 تضبط لونًا. تركّبها داخل className. ويمسح Tailwind ملفاتك، ويعثر على الـ classes التي استخدمتها، ويولّد stylesheet لا يحتوي إلا عليها.
ثبّته
لـ Tailwind v4 plugin رسمي لـ Vite، والإعداد ثلاث خطوات. ثبّت الحزم:
npm install tailwindcss @tailwindcss/viteأضف الـ plugin إلى إعدادات Vite:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import tailwindcss from '@tailwindcss/vite';
export default defineConfig({
plugins: [react(), tailwindcss()],
});واستورد Tailwind في أعلى ملف الأنماط الرئيسي، الذي يستورده main.jsx أصلًا:
@import "tailwindcss";هذا كل شيء. لا يوجد tailwind.config.js ولا إعدادات PostCSS في v4 افتراضيًا. يعثر Tailwind على ملفات المصدر بنفسه. ويعيد Vite تشغيل خادم التطوير تلقائيًا حين تحفظ vite.config.js؛ وإن لم تظهر الأنماط بعد ذلك، فأوقفه وشغّل npm run dev من جديد.
قراءة الـ utility classes
هذا هو الهيكل الخارجي لـ Watchlist:
<main className="mx-auto max-w-xl px-4 py-10">
<h1 className="text-3xl font-bold tracking-tight text-slate-900">Watchlist</h1>
<p className="mt-1 text-sm text-slate-500">{left} to watch</p>
{/* …form, filters, list */}
</main>الأسماء مختصرة لكنها منهجية، وبعد يوم واحد ستقرؤها بسرعة قراءة CSS:
- المسافات تستخدم مقياسًا كل خطوة فيه 0.25rem:
p-4حشو مقداره 1rem، وmt-1هامش علوي مقداره 0.25rem، وpx-4حشو أفقي. وmx-autoيوسّط الكتلة. - الأحجام والخطوط:
max-w-xlتحدّ العرض، وtext-3xlتضبط حجم الخط، وfont-boldسُمكه. - الألوان تأتي من لوحة بدرجات من 50 إلى 950:
text-slate-500وbg-indigo-600.
ويستطيع محرّرك المساعدة: إضافة Tailwind CSS IntelliSense الرسمية لـ VS Code تكمل أسماء الـ classes تلقائيًا وتعرض الـ CSS الذي تنتجه كل واحدة حين تمرّر المؤشّر عليها.
الـ variants: الشروط في بادئة
ضع بادئة قبل الـ class لتطبيقه تحت شرط فقط:
<li className="flex items-center gap-3 rounded-lg border border-slate-200 bg-white p-3 dark:border-slate-700 dark:bg-slate-800">
<span className={movie.watched ? 'flex-1 text-slate-400 line-through' : 'flex-1 text-slate-900 dark:text-slate-100'}>
{movie.title}
</span>
<button
aria-pressed={movie.watched}
onClick={() => onToggle(movie.id)}
className="rounded-md border border-slate-300 px-3 py-1 text-sm hover:bg-slate-100 focus-visible:outline-2 focus-visible:outline-indigo-500 aria-pressed:border-indigo-600 aria-pressed:bg-indigo-600 aria-pressed:text-white"
>
{movie.watched ? 'Watched' : 'Mark watched'}
</button>
</li>- تتعامل
hover:وfocus-visible:مع حالات التفاعل. امنح العناصر التفاعلية دائمًا نمط تركيز مرئيًا لمستخدمي لوحة المفاتيح. - تُطبَّق
dark:حين يكون نظام المستخدم في الوضع الداكن (تتبعprefers-color-schemeافتراضيًا). - تُطبَّق
aria-pressed:حين تكونaria-pressedفي العنصر true. فيتبع مظهر زر التبديل الخاصية نفسها التي تعلنها الـ screen readers، ولا يمكن أن يختلفا. - نقاط التوقّف تبدأ من الجوّال:
sm:وmd:وlg:تُطبَّق من ذلك العرض فما فوق، لذا اكتب تخطيط الهاتف أولًا، وأضف البادئات للشاشات الأكبر.
أمّا الشروط المعتمدة على الـ state في React، مثل movie.watched على الـ span، فهي معامل ثلاثي عادي يختار بين نصّين كاملين من الـ classes.
الـ design tokens الخاصة بك
خصّص السمة داخل CSS باستخدام @theme. كل متغيّر يصبح utilities تلقائيًا:
@import "tailwindcss";
@theme {
--color-brand-500: oklch(0.62 0.19 285);
--color-brand-600: oklch(0.55 0.21 285);
--font-display: "Inter", system-ui, sans-serif;
}الآن تعمل bg-brand-600 وtext-brand-500 وfont-display كأنها classes مدمجة. عرّف عددًا قليلًا من الـ tokens للون علامتك التجارية وخطوطك، بدل أن تنثر قيمًا عابرة مثل bg-[#6d28d9] في أنحاء الـ components.
المقايضة
قوائم الـ classes الطويلة هي الثمن الصريح. قد يحمل زر منسَّق خمسة عشر class. والحلّ ليس إخفاءها في CSS باستخدام @apply؛ بل ما تدرّبت عليه طوال الدورة: استخرج component. الـ component المسمّى Button يحمل هذه الـ classes الخمسة عشر في مكان واحد، وفي كل مكان آخر تكتب <Button>. الـ components هي وحدة إعادة الاستخدام في React، للأنماط تمامًا كما للسلوك.
Tailwind ليس الجواب الجيد الوحيد. الـ CSS Modules (مدمجة في Vite: سمِّ الملف MovieItem.module.css واستورده) تعطيك أسماء classes محصورة النطاق مع CSS عادي، وكثير من الفرق تفضّلها. والمهارات تنتقل في الحالتين: markup دلالي، ومقياس متّسق للمسافات والألوان، وحالات تركيز مرئية، وتخطيط يعمل على الهاتف أولًا.
صار Watchlist يبدو كمنتج حقيقي. بقي شيء واحد: وضعه على الإنترنت، وهذا موضوع الدرس التالي.
أهم النقاط
- يحتاج Tailwind v4 إلى
npm install tailwindcss @tailwindcss/vite، والـ plugin داخل vite.config.js، و@import "tailwindcss";في ملف CSS؛ ولا حاجة إلى ملف إعدادات. - كل utility class تقابل تصريح CSS واحدًا على مقياس مشترك، مثل
p-4لحشو مقداره 1rem، وtext-slate-600للون من لوحة الألوان. - البادئات تطبّق الـ class بشرط، مثل
md:فوق نقطة توقّف، وhover:عند التمرير، وdark:في الوضع الداكن، وaria-pressed:حين تكون تلك الخاصية true. - يعثر Tailwind على الـ classes بمسح ملفاتك كنص، لذا اكتب دائمًا أسماء classes كاملة ولا تبنِها أبدًا بدمج النصوص.
- خصّص الألوان والخطوط وغيرها من الـ tokens بمتغيّرات CSS داخل كتلة
@theme.