تُنسَّق مكوّنات React بـ CSS العادية. أعطِ العناصر صنفًا بالـ prop المسماة className واكتب القواعد في ورقة أنماط، واستخدم الـ prop المسماة style للقيم الآتية من JavaScript، والجأ إلى CSS Modules أو Tailwind عندما يكبر المشروع. يعرّف المثال أدناه CSS الخاصة به في عنصر <style> ليتمكن من العمل في هذه الصفحة؛ أما في مشروع فستكون في ملف .css.
غيّر #2563eb إلى #16a34a في القاعدة .btn فيتحول الزر إلى الأخضر. كل ما تعرفه عن CSS ينطبق: المحددات واستعلامات الوسائط وحالات التمرير والحركات.
className لا class
تتحول JSX إلى JavaScript، حيث class كلمة محجوزة، لذا تستخدم React الاسم className، وهو الاسم الذي يستخدمه DOM نفسه لهذه الخاصية (element.className). ما زال المتصفح يستقبل سمة class عادية. وتنطبق إعادة التسمية نفسها على htmlFor بدلًا من for في التسميات. تقبل React 19 الاسم class مع تحذير أثناء التطوير، لكن className هو الاسم الصحيح وهو ما يستخدمه كل مشروع.
تأخذ className نصًا، فتُفصل الأصناف المتعددة بمسافات: className="btn btn-primary". ولأن القيمة JavaScript، يمكن حسابها.
الأصناف الشرطية
لإضافة صنف في بعض الحالات فقط، ابنِ النص بمعامل ثلاثي أو بقالب نصي. شريط التبويبات الذي يميّز التبويب المحدد هو الحالة الكلاسيكية.
انقر Lessons فينتقل الخط السفلي، لأن نص ذلك الزر وحده يحتوي الآن على active. يحصل Reviews على الصنف disabled والسمة disabled، فيكون رماديًا ويتجاهل النقرات. يترك القالب النصي مسافات زائدة عندما يكون الشرط خاطئًا، وتتجاهلها المتصفحات. وعندما يحتوي مكوّن على أصناف شرطية كثيرة، تكون المصفوفة أكثر ترتيبًا:
const classes = ['tab', isActive && 'active', isDisabled && 'disabled']
.filter(Boolean)
.join(' ');
تستخدم مشاريع كثيرة الحزمة الصغيرة clsx للمهمة نفسها: clsx('tab', { active: isActive, disabled: isDisabled }).
الـ prop المسماة style
تأخذ الـ prop المسماة style كائن JavaScript، لا نصًا. ولهذا فيها أقواس مزدوجة: الزوج الخارجي يفتح تعبير JavaScript، والزوج الداخلي هو الكائن نفسه. أسماء الخصائص بنمط camelCase (backgroundColor وfontSize وborderRadius)، والقيم نصوص أو أرقام. الرقم في خاصية طول يصبح بكسلات، فيعني padding: 12 القيمة 12px.
تتألق الأنماط المضمّنة عندما تُحسب القيمة وقت العرض، مثل عرض شريط التقدم.
انقر Add 10% حتى يمتلئ الشريط فيتحول إلى الأخضر. لاحظ width: percent + '%': الرقم المجرد يعني البكسلات، لذا توضع الوحدات غير px في نص. والخصائص التي لا وحدة لها تبقى أرقامًا مجردة، مثل opacity: 0.8 وzIndex: 2 وflexGrow: 1 وlineHeight: 1.5 (وهي معامل ضرب لا بكسلات).
للأنماط المضمّنة حدود. لا تستطيع التعبير عن :hover و:focus واستعلامات الوسائط وحركات keyframe، ويحمل كل عنصر نسخته الخاصة من القواعد. استخدمها للقيم الديناميكية واترك الباقي في CSS.
متغيرات CSS من props
حل وسط جيد هو ضبط خاصية CSS مخصصة في style واستخدامها من ورقة الأنماط. تبقى القواعد في CSS، بما فيها حالات التمرير، ولا تأتي إلا القيمة من JavaScript.
اختر لونًا جديدًا فيتبعه الزران كلاهما، ويظل تلوين التمرير يعمل لأنه CSS عادية. تحتفظ أسماء الخصائص المخصصة بشرطاتها في كائن style، فتوضع بين علامتي تنصيص: { '--accent': accent }.
ملفات CSS
في مشروع حقيقي تكتب الأنماط في ملفات .css وتستوردها من المكوّن الذي يحتاجها. مع Vite يكفي الاستيراد: ترى أداة البناء import './Button.css'، فتضيف ورقة الأنماط إلى الصفحة أثناء التطوير، وتجمعها في ملف CSS للإنتاج. لا يوجد تصدير لتستخدمه؛ فالاستيراد موجود لأثره الجانبي فقط.
// Button.jsx
import './Button.css';
export default function Button({ children }) {
return <button className="button">{children}</button>;
}
/* Button.css */
.button {
background: #2563eb;
color: white;
border-radius: 6px;
}
المشكلة أن كل ورقة أنماط مستوردة عامة. القاعدة .button في Button.css تنسّق كل عنصر بهذا الصنف، في أي مكان من التطبيق، وملفان يعرّفان كلاهما .title يكتب أحدهما فوق الآخر. تتعامل الفرق مع هذا باصطلاحات تسمية مثل BEM (.card__title)، أو بـ CSS Modules.
CSS Modules
الملف الذي ينتهي بـ .module.css هو CSS Module. استيراده يعطيك كائنًا يربط كل اسم صنف كتبته باسم فريد مولّد، فلا ينطبق الصنف إلا حيث تستخدمه. يدعم Vite وNext.js ذلك دون أي إعداد.
// Card.jsx
import styles from './Card.module.css';
export default function Card({ title, children, featured }) {
return (
<section className={featured ? `${styles.card} ${styles.featured}` : styles.card}>
<h3 className={styles.title}>{title}</h3>
{children}
</section>
);
}
/* Card.module.css */
.card { border: 1px solid #ddd; border-radius: 10px; padding: 16px; }
.featured { border-color: #2563eb; }
.title { margin: 0; }
في المتصفح يظهر الصنف بشكل مثل _card_1x2ab_1. يمكن لمكوّن آخر أن يعرّف .title خاصًا به ولا يتعارض الاثنان أبدًا. تكتب CSS عادية، بما فيها قواعد التمرير واستعلامات الوسائط، ولهذا تُعد CSS Modules خيارًا افتراضيًا شائعًا لأنماط المكوّنات.
Tailwind CSS مع React
يتبع Tailwind نهجًا مختلفًا: بدلًا من كتابة القواعد، تركّب أصنافًا خدمية صغيرة مباشرة في className. يضبط px-4 الحشوة الأفقية، ويدوّر rounded-lg الزوايا، ويغيّر hover:bg-blue-700 الخلفية عند التمرير. يفحص Tailwind ملفاتك ويولّد CSS للأصناف التي تستخدمها فقط.
إعداده في تطبيق React مع Vite (الإصدار v4 من Tailwind) يتطلب ثلاث خطوات:
npm install tailwindcss @tailwindcss/vite
// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import tailwindcss from '@tailwindcss/vite';
export default defineConfig({
plugins: [react(), tailwindcss()],
});
/* src/index.css, imported from main.jsx */
@import "tailwindcss";
ثم نسّق المكوّنات بالأصناف:
function UpgradeCard({ active }) {
return (
<div className="max-w-xs rounded-xl border border-gray-200 p-4">
<h3 className="mb-2 text-lg font-semibold">Pro plan</h3>
<button
className={`rounded-md px-4 py-2 text-white ${active ? 'bg-green-600' : 'bg-blue-600 hover:bg-blue-700'}`}
>
{active ? 'Active' : 'Upgrade'}
</button>
</div>
);
}
Tailwind غير متاح في محررات هذه الصفحة، لذا يُعرض ككود ثابت. تعمل الأصناف الشرطية تمامًا كما في مثال التبويبات أعلاه، لكن اكتب كل اسم صنف كاملًا: يجد Tailwind الأصناف بفحص نص مصدرك، فالاسم المركّب وقت التشغيل مثل `bg-${color}-600` لا يُولَّد أبدًا.
متى تستخدم أيًا منها
- ملفات CSS العادية تناسب التطبيقات الصغيرة والأنماط العامة (إعادة الضبط والخطوط وتخطيط الصفحة). انتبه لتعارض أسماء الأصناف كلما كبر التطبيق.
- CSS Modules تناسب معظم أنماط المكوّنات: CSS عادية، محصورة النطاق تلقائيًا، دون مكتبة إضافية.
- Tailwind يناسب الفرق التي تفضّل التنسيق داخل الترميز وتريد مقياس تصميم مشتركًا للمسافات والألوان.
- الـ prop المسماة
styleللقيم المحسوبة وقت العرض: عرض، أو موضع، أو لون اختاره المستخدم. اقرنها بمتغيرات CSS لتُبقي قواعد التمرير والوسائط في CSS.
يمكنك المزج بينها. يحتوي التطبيق المعتاد على ورقة أنماط عامة واحدة، وCSS Modules أو Tailwind للمكوّنات، وبعض الأنماط المضمّنة للقيم الديناميكية. ومكتبات CSS-in-JS مثل styled-components وEmotion خيار آخر ستصادفه في المشاريع الأقدم. تُظهر صفحة تثبيت React كيف تبدأ مشروع Vite تعمل فيه كل هذه الطرق.
الأسئلة الشائعة
لماذا تستخدم React الاسم className بدلًا من class؟
تتحول JSX إلى JavaScript، وclass كلمة محجوزة هناك. تستخدم React اسم خاصية DOM، className، وهو أيضًا اسم element.className في JavaScript العادية. وما زالت تتحول إلى سمة class عادية في HTML.
كيف أكتب أنماطًا مضمّنة في React؟
مرّر كائنًا إلى الـ prop المسماة style، بأسماء خصائص بنمط camelCase: style={{ backgroundColor: 'teal', fontSize: 18 }}. الأقواس الخارجية تفتح تعبير JavaScript والداخلية هي الكائن. وتصبح الأرقام بكسلات في الخصائص التي تأخذ طولًا.
كيف أضيف صنفًا بشرط في React؟
ابنِ النص في JavaScript: className={isActive ? 'tab active' : 'tab'} أو قالبًا نصيًا مثل className={`tab ${isActive ? 'active' : ''}`}. ومع شروط كثيرة، تُبقي أداة مساعدة صغيرة مثل الحزمة clsx الكود سهل القراءة.
هل ملفات CSS في React عامة؟
نعم. يضيف import './App.css' ورقة الأنماط إلى الصفحة كلها، فقد تتعارض أسماء أصنافها مع ملفات أخرى. أما CSS Modules (App.module.css) فتعيد تسمية كل صنف باسم فريد فلا ينطبق إلا حيث تستورده.
كيف أستخدم Tailwind مع React؟
في مشروع Vite، ثبّت tailwindcss و@tailwindcss/vite، وأضف الإضافة إلى vite.config.js، وضع @import "tailwindcss"; في أعلى ملف CSS الرئيسي. ثم اكتب الأصناف الخدمية في className، مثل className="px-4 py-2 rounded bg-blue-600 text-white".