التنسيق في React
مقدمة إلى التنسيق في React
يوفر React عدة طرق لتنسيق المكونات، كل منها له مزايا فريدة. يستكشف هذا الدرس الأنماط المضمنة ووحدات CSS وstyled-components وTailwind CSS ومكتبات CSS-in-JS، مما يساعدك على اختيار النهج المناسب لمشاريعك.
CSS التقليدي مع className
النهج الأبسط يستخدم ملفات CSS العادية مع خاصية className:
// src/components/Button.jsx
import './Button.css';
function Button({ children, variant = 'primary' }) {
return (
<button className={`btn btn-${variant}`}>
{children}
</button>
);
}
export default Button;
/* src/components/Button.css */
.btn {
padding: 0.75rem 1.5rem;
border: none;
border-radius: 0.375rem;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
}
.btn-primary {
background-color: #3b82f6;
color: white;
}
.btn-primary:hover {
background-color: #2563eb;
}
.btn-secondary {
background-color: #6b7280;
color: white;
}
.btn-secondary:hover {
background-color: #4b5563;
}
تحذير: CSS التقليدي له نطاق عام. يمكن أن تتعارض أسماء الفئات عبر المكونات. استخدم اصطلاحات التسمية مثل BEM أو وحدات CSS لتجنب التعارضات.
وحدات CSS: التنسيق المحدد النطاق
وحدات CSS تحدد نطاق الأنماط تلقائيًا للمكونات، مما يمنع تعارضات التسمية:
// src/components/Card.jsx
import styles from './Card.module.css';
function Card({ title, content, featured }) {
return (
<div className={`${styles.card} ${featured ? styles.featured : ''}`}>
<h3 className={styles.title}>{title}</h3>
<p className={styles.content}>{content}</p>
</div>
);
}
// بديل: استخدام دمج المصفوفة
function Card2({ title, content, featured }) {
const cardClasses = [
styles.card,
featured && styles.featured
].filter(Boolean).join(' ');
return (
<div className={cardClasses}>
<h3 className={styles.title}>{title}</h3>
<p className={styles.content}>{content}</p>
</div>
);
}
export default Card;
/* src/components/Card.module.css */
.card {
background: white;
border-radius: 0.5rem;
padding: 1.5rem;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
transition: transform 0.2s, box-shadow 0.2s;
}
.card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.featured {
border: 2px solid #3b82f6;
background: linear-gradient(to bottom right, #eff6ff, white);
}
.title {
font-size: 1.25rem;
font-weight: 700;
color: #1f2937;
margin-bottom: 0.5rem;
}
.content {
color: #6b7280;
line-height: 1.6;
}
نصيحة: وحدات CSS تعمل مع Create React App و Vite مباشرةً. فقط قم بتسمية ملفاتك *.module.css.
الأنماط المضمنة: كائنات JavaScript
يدعم React الأنماط المضمنة باستخدام كائنات JavaScript مع خصائص camelCase:
function InlineStylesExample() {
const containerStyle = {
backgroundColor: '#f3f4f6',
padding: '2rem',
borderRadius: '0.5rem',
maxWidth: '600px',
margin: '0 auto'
};
const headingStyle = {
fontSize: '2rem',
fontWeight: 'bold',
color: '#1f2937',
marginBottom: '1rem'
};
const textStyle = {
color: '#4b5563',
lineHeight: 1.6
};
return (
<div style={containerStyle}>
<h2 style={headingStyle}>الأنماط المضمنة</h2>
<p style={textStyle}>يستخدم هذا المكون الأنماط المضمنة.</p>
</div>
);
}
// الأنماط المضمنة الديناميكية بناءً على الخصائص
function Alert({ type, message }) {
const baseStyle = {
padding: '1rem',
borderRadius: '0.375rem',
marginBottom: '1rem',
border: '1px solid'
};
const typeStyles = {
success: {
backgroundColor: '#d1fae5',
borderColor: '#10b981',
color: '#065f46'
},
error: {
backgroundColor: '#fee2e2',
borderColor: '#ef4444',
color: '#991b1b'
},
warning: {
backgroundColor: '#fef3c7',
borderColor: '#f59e0b',
color: '#92400e'
}
};
const alertStyle = { ...baseStyle, ...typeStyles[type] };
return <div style={alertStyle}>{message}</div>;
}
ملاحظة: الأنماط المضمنة لا تدعم الفئات الزائفة (:hover، :focus) أو استعلامات الوسائط. استخدم CSS/وحدات CSS لهذه الميزات.
styled-components: CSS-in-JS
styled-components هي مكتبة CSS-in-JS شائعة تتيح لك كتابة CSS مباشرةً في JavaScript:
// التثبيت
npm install styled-components
// src/components/StyledButton.jsx
import styled from 'styled-components';
// مكون منسق أساسي
const StyledButton = styled.button`
padding: 0.75rem 1.5rem;
border: none;
border-radius: 0.375rem;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
background-color: #3b82f6;
color: white;
&:hover {
background-color: #2563eb;
transform: translateY(-1px);
}
&:active {
transform: translateY(0);
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
`;
// مكون منسق بالخصائص
const Button = styled.button`
padding: ${props => props.size === 'large' ? '1rem 2rem' : '0.5rem 1rem'};
background-color: ${props => {
switch(props.variant) {
case 'primary': return '#3b82f6';
case 'success': return '#10b981';
case 'danger': return '#ef4444';
default: return '#6b7280';
}
}};
color: white;
border: none;
border-radius: 0.375rem;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
&:hover {
opacity: 0.9;
transform: translateY(-2px);
}
`;
// توسيع المكونات المنسقة
const IconButton = styled(Button)`
display: flex;
align-items: center;
gap: 0.5rem;
svg {
width: 1.25rem;
height: 1.25rem;
}
`;
// الاستخدام
function App() {
return (
<div>
<StyledButton>انقر هنا</StyledButton>
<Button variant="primary" size="large">أساسي</Button>
<Button variant="success">نجاح</Button>
<Button variant="danger">خطر</Button>
<IconButton variant="primary">
<span>➤</span> مع أيقونة
</IconButton>
</div>
);
}
أفضل ممارسة: أنشئ كائن سمة للألوان والتباعد والطباعة المتسقة عبر styled-components.
Tailwind CSS مع React
Tailwind هو إطار عمل CSS يعتمد على الأدوات ويوفر فئات جاهزة:
// التثبيت لـ Vite
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
// tailwind.config.js
export default {
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
// src/index.css
@tailwind base;
@tailwind components;
@tailwind utilities;
// src/components/TailwindCard.jsx
function TailwindCard({ title, description, image, featured }) {
return (
<div className={`
rounded-lg shadow-md overflow-hidden transition-all duration-300
hover:shadow-xl hover:-translate-y-1
${featured ? 'ring-2 ring-blue-500 bg-gradient-to-br from-blue-50 to-white' : 'bg-white'}
`}>
<img
src={image}
alt={title}
className="w-full h-48 object-cover"
/>
<div className="p-6">
<h3 className="text-xl font-bold text-gray-800 mb-2">
{title}
</h3>
<p className="text-gray-600 leading-relaxed">
{description}
</p>
<button className="
mt-4 px-6 py-2 bg-blue-500 text-white font-semibold rounded-md
hover:bg-blue-600 active:bg-blue-700 transition-colors
focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2
">
معرفة المزيد
</button>
</div>
</div>
);
}
// مكون زر قابل لإعادة الاستخدام مع Tailwind
function Button({ children, variant = 'primary', size = 'md', ...props }) {
const baseClasses = 'font-semibold rounded-md transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2';
const variantClasses = {
primary: 'bg-blue-500 hover:bg-blue-600 text-white focus:ring-blue-500',
secondary: 'bg-gray-500 hover:bg-gray-600 text-white focus:ring-gray-500',
success: 'bg-green-500 hover:bg-green-600 text-white focus:ring-green-500',
danger: 'bg-red-500 hover:bg-red-600 text-white focus:ring-red-500',
outline: 'border-2 border-blue-500 text-blue-500 hover:bg-blue-50 focus:ring-blue-500'
};
const sizeClasses = {
sm: 'px-3 py-1.5 text-sm',
md: 'px-4 py-2',
lg: 'px-6 py-3 text-lg'
};
const className = `${baseClasses} ${variantClasses[variant]} ${sizeClasses[size]}`;
return (
<button className={className} {...props}>
{children}
</button>
);
}
التنسيق الشرطي والديناميكي
تقنيات لتطبيق الأنماط بشكل شرطي:
// استخدام القوالب الحرفية
function ConditionalStyling({ isActive, isDisabled, size }) {
return (
<button
className={`
btn
${isActive ? 'btn-active' : 'btn-inactive'}
${isDisabled ? 'btn-disabled' : ''}
${size === 'large' ? 'btn-lg' : 'btn-md'}
`}
>
انقر هنا
</button>
);
}
// استخدام مكتبة classnames (موصى به)
import classNames from 'classnames';
function BetterConditional({ isActive, isDisabled, size }) {
const buttonClasses = classNames(
'btn',
{
'btn-active': isActive,
'btn-inactive': !isActive,
'btn-disabled': isDisabled,
'btn-lg': size === 'large',
'btn-md': size === 'medium',
'btn-sm': size === 'small'
}
);
return <button className={buttonClasses}>انقر هنا</button>;
}
// الأنماط المضمنة الديناميكية
function ProgressBar({ progress, color = 'blue' }) {
const colorMap = {
blue: '#3b82f6',
green: '#10b981',
red: '#ef4444'
};
return (
<div style={{
width: '100%',
height: '1rem',
backgroundColor: '#e5e7eb',
borderRadius: '0.25rem',
overflow: 'hidden'
}}>
<div style={{
width: `${progress}%`,
height: '100%',
backgroundColor: colorMap[color],
transition: 'width 0.3s ease'
}} />
</div>
);
}
متغيرات CSS مع React
استخدم خصائص CSS المخصصة للسمات:
// src/App.jsx
import { useState } from 'react';
import './theme.css';
function App() {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
};
return (
<div data-theme={theme}>
<button onClick={toggleTheme}>تبديل السمة</button>
<div className="card">
<h2>بطاقة بسمة</h2>
<p>تتكيف هذه البطاقة مع السمة.</p>
</div>
</div>
);
}
/* src/theme.css */
:root {
--bg-primary: #ffffff;
--bg-secondary: #f3f4f6;
--text-primary: #1f2937;
--text-secondary: #6b7280;
--border-color: #e5e7eb;
--accent: #3b82f6;
}
[data-theme="dark"] {
--bg-primary: #1f2937;
--bg-secondary: #111827;
--text-primary: #f9fafb;
--text-secondary: #d1d5db;
--border-color: #374151;
--accent: #60a5fa;
}
body {
background-color: var(--bg-primary);
color: var(--text-primary);
}
.card {
background-color: var(--bg-secondary);
border: 1px solid var(--border-color);
padding: 1.5rem;
border-radius: 0.5rem;
}
.card h2 {
color: var(--accent);
}
تمرين 1: تطبيق قائمة المهام بوحدات CSS
المهمة: أنشئ تطبيق قائمة مهام باستخدام وحدات CSS:
- أنشئ مكونات TodoItem و TodoList و AddTodo
- استخدم ملفات
*.module.cssمنفصلة لكل مكون - نسّق المهام المكتملة بشكل مختلف (خط مشطوب، شفافية)
- أضف تأثيرات التمرير على عناصر المهام
- أنشئ نظام أولوية بألوان مختلفة (عالية/متوسطة/منخفضة)
تمرين 2: بطاقة منتج Tailwind
المهمة: بناء مكون بطاقة منتج باستخدام Tailwind CSS:
- قم بتضمين صورة المنتج والعنوان والسعر والتقييم وزر "أضف إلى السلة"
- أضف تصميم متجاوب (يتكدس على الجوال، شبكة على سطح المكتب)
- نفّذ نوع "مميز" بتنسيق مختلف
- أضف تأثيرات التمرير والانتقالات
- أنشئ شارة خصم لعناصر التخفيض
تمرين 3: مبدل السمات
المهمة: بناء مبدل سمات باستخدام متغيرات CSS:
- أنشئ سمات فاتحة ومظلمة وعالية التباين
- خزّن السمة المحددة في localStorage
- طبّق السمة عند التحميل الأولي بناءً على التفضيل المخزن
- أنشئ قائمة منسدلة للاختيار بين السمات
- يجب أن تؤثر السمة على جميع المكونات (الأزرار، البطاقات، النص)
الخلاصة
في هذا الدرس، استكشفت طرق التنسيق المختلفة في React:
- CSS التقليدي مع
classNameلاحتياجات التنسيق البسيطة - وحدات CSS للأنماط المحددة النطاق والخاصة بالمكون
- الأنماط المضمنة للتنسيق الديناميكي المدفوع بـ JavaScript
- styled-components لـ CSS-in-JS القوي بميزات CSS الكاملة
- Tailwind CSS للتطوير السريع بفئات الأدوات
- تقنيات التنسيق الشرطي وإدارة الفئات الديناميكية
- متغيرات CSS للسمات وأنظمة التصميم
في الدرس التالي، سنتعمق في دورة حياة المكونات وتحسين الأداء باستخدام React.memo و useMemo و useCallback.