@apply واستخراج المكونات
@apply واستخراج المكونات
بينما يشجع Tailwind على نهج الأدوات أولاً مع الفئات في HTML الخاص بك، هناك أوقات تحتاج فيها إلى استخراج الأنماط المتكررة إلى مكونات قابلة لإعادة الاستخدام أو إنشاء فئات أدوات مخصصة. توجيه @apply هو أداتك لهذا—ولكن يجب استخدامه بحكمة.
في هذا الدرس، سنستكشف متى وكيف نستخدم @apply، ونفهم الفلسفة وراء استخراج المكونات، ونتعلم أفضل الممارسات لتحقيق التوازن بين CSS الأدوات أولاً والأنماط القائمة على المكونات.
ما هو @apply؟
يتيح لك توجيه @apply تكوين فئات أدوات Tailwind في فئات CSS مخصصة. يستخرج فئات الأدوات من HTML الخاص بك وينقلها إلى CSS الخاص بك:
مثال @apply الأساسي
/* في ملف CSS الخاص بك */
.btn-primary {
@apply px-6 py-3 bg-blue-500 text-white rounded-lg;
@apply hover:bg-blue-600 focus:outline-none focus:ring-2;
@apply focus:ring-blue-500 focus:ring-offset-2;
@apply transition-colors duration-200;
}
الآن في HTML الخاص بك، بدلاً من هذا:
قبل @apply (HTML مطوّل)
<button class="px-6 py-3 bg-blue-500 text-white rounded-lg hover:bg-blue-600
focus:outline-none focus:ring-2 focus:ring-blue-500
focus:ring-offset-2 transition-colors duration-200">
اضغط هنا
</button>
يمكنك كتابة هذا:
بعد @apply (HTML نظيف)
<button class="btn-primary">
اضغط هنا
</button>
متى تستخدم @apply
قبل استخدام @apply، اسأل نفسك: "هل أستخدم هذا النمط بالضبط في أماكن متعددة؟" إليك حالات الاستخدام الجيدة:
1. أنماط المكونات المتكررة
نظام الأزرار مع @apply
/* أنماط الزر الأساسية */
.btn {
@apply inline-flex items-center justify-center;
@apply px-4 py-2 rounded-md font-medium;
@apply transition-colors duration-200;
@apply focus:outline-none focus:ring-2 focus:ring-offset-2;
}
/* متغيرات الزر */
.btn-primary {
@apply btn bg-blue-500 text-white;
@apply hover:bg-blue-600 focus:ring-blue-500;
}
.btn-secondary {
@apply btn bg-gray-200 text-gray-800;
@apply hover:bg-gray-300 focus:ring-gray-500;
}
.btn-danger {
@apply btn bg-red-500 text-white;
@apply hover:bg-red-600 focus:ring-red-500;
}
/* أحجام الزر */
.btn-sm {
@apply px-3 py-1.5 text-sm;
}
.btn-lg {
@apply px-6 py-3 text-lg;
}
استخدام فئات الأزرار
<button class="btn btn-primary">زر أساسي</button>
<button class="btn btn-secondary btn-sm">ثانوي صغير</button>
<button class="btn btn-danger btn-lg">خطر كبير</button>
2. عناصر النماذج
فئات مكونات النماذج
/* أنماط حقل الإدخال الأساسية */
.input {
@apply w-full px-4 py-2 border border-gray-300 rounded-md;
@apply focus:outline-none focus:ring-2 focus:ring-blue-500;
@apply focus:border-transparent transition-all duration-200;
}
.input-error {
@apply input border-red-500 focus:ring-red-500;
}
/* أنماط التسمية */
.label {
@apply block text-sm font-medium text-gray-700 mb-1;
}
.label-required::after {
content: " *";
@apply text-red-500;
}
/* مجموعة النموذج */
.form-group {
@apply mb-4;
}
/* رسالة الخطأ */
.error-message {
@apply mt-1 text-sm text-red-600;
}
استخدام فئات النماذج
<div class="form-group">
<label class="label label-required">البريد الإلكتروني</label>
<input type="email" class="input" placeholder="you@example.com">
</div>
<div class="form-group">
<label class="label">كلمة المرور</label>
<input type="password" class="input-error">
<p class="error-message">يجب أن تكون كلمة المرور 8 أحرف على الأقل</p>
</div>
3. مكونات البطاقات
نظام البطاقات مع @apply
/* بطاقة أساسية */
.card {
@apply bg-white rounded-lg shadow-md overflow-hidden;
@apply border border-gray-200;
}
/* أقسام البطاقة */
.card-header {
@apply px-6 py-4 border-b border-gray-200;
@apply bg-gray-50;
}
.card-body {
@apply px-6 py-4;
}
.card-footer {
@apply px-6 py-4 border-t border-gray-200;
@apply bg-gray-50;
}
/* عنوان البطاقة */
.card-title {
@apply text-xl font-semibold text-gray-900;
}
/* متغيرات البطاقة */
.card-hover {
@apply card transition-all duration-300;
@apply hover:shadow-lg hover:-translate-y-1;
}
.card-interactive {
@apply card cursor-pointer;
@apply hover:border-blue-500 hover:shadow-lg;
}
متى لا تستخدم @apply
فهم متى تتجنب @apply مهم بنفس القدر مثل معرفة متى تستخدمه:
- أنماط لمرة واحدة: إذا كنت تستخدم نمطًا مرة واحدة فقط، احتفظ بالأدوات في HTML
- توليفات بسيطة: 2-3 أدوات لا تبرر الاستخراج
- أنماط خاصة بالصفحة: التخطيطات الفريدة يجب أن تبقى في HTML
- مكونات الإطار: React، Vue، إلخ. تتعامل مع التركيب بشكل أفضل
- التحسين المبكر: انتظر حتى تتكرر الأنماط 3+ مرات
استخدام سيئ لـ @apply (لا تفعل هذا)
/* لا تفعل: استخراج كل شيء */
.my-div {
@apply w-full h-full bg-white p-4 m-2 rounded shadow;
}
/* لا تفعل: أنماط لمرة واحدة */
.about-page-hero {
@apply h-screen flex items-center justify-center bg-gradient-to-r from-blue-500 to-purple-600;
}
/* لا تفعل: توليفات بسيطة */
.flex-center {
@apply flex items-center justify-center;
}
/* من الأفضل الاحتفاظ بهذا في HTML أو كمكون */
توجيه @layer
عند استخدام @apply، نظم أنماطك المخصصة مع توجيه @layer. يخبر هذا Tailwind أين ينتمي CSS المخصص الخاص بك في التسلسل:
استخدام @layer للتنظيم
/* في ملف CSS الرئيسي الخاص بك */
/* طبقة Base - افتراضيات عناصر HTML */
@layer base {
h1 {
@apply text-4xl font-bold text-gray-900;
}
h2 {
@apply text-3xl font-semibold text-gray-800;
}
a {
@apply text-blue-600 hover:text-blue-800 underline;
}
body {
@apply font-sans text-gray-900 antialiased;
}
}
/* طبقة Components - مكونات قابلة لإعادة الاستخدام */
@layer components {
.btn {
@apply px-4 py-2 rounded-md font-medium;
@apply transition-colors duration-200;
}
.btn-primary {
@apply btn bg-blue-500 text-white;
@apply hover:bg-blue-600;
}
.card {
@apply bg-white rounded-lg shadow-md p-6;
}
.input {
@apply w-full px-4 py-2 border rounded-md;
@apply focus:outline-none focus:ring-2;
}
}
/* طبقة Utilities - فئات أدوات مخصصة */
@layer utilities {
.text-shadow {
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
}
.bg-grid {
background-image: repeating-linear-gradient(
0deg, #e5e7eb 0px, #e5e7eb 1px,
transparent 1px, transparent 20px
);
}
.scrollbar-hide {
-ms-overflow-style: none;
scrollbar-width: none;
}
.scrollbar-hide::-webkit-scrollbar {
display: none;
}
}
- base: يتم تحميله أولاً، لافتراضيات عناصر HTML
- components: يتم تحميله ثانيًا، لفئات المكونات
- utilities: يتم تحميله أخيرًا، أعلى خصوصية
يمكن للأدوات تجاوز المكونات، ويمكن للمكونات تجاوز أنماط base.
استخراج الأنماط المعقدة
أحيانًا تحتاج إلى دمج @apply مع CSS العادي للأنماط المعقدة:
مكون معقد مع أنماط مختلطة
@layer components {
/* مكون القائمة المنسدلة */
.dropdown {
@apply relative inline-block;
}
.dropdown-trigger {
@apply px-4 py-2 bg-white border border-gray-300 rounded-md;
@apply hover:bg-gray-50 focus:outline-none;
cursor: pointer;
}
.dropdown-menu {
@apply absolute left-0 mt-2 w-56 rounded-md shadow-lg;
@apply bg-white ring-1 ring-black ring-opacity-5;
@apply opacity-0 invisible transition-all duration-200;
@apply transform origin-top-left scale-95;
z-index: 1000;
}
.dropdown.open .dropdown-menu {
@apply opacity-100 visible scale-100;
}
.dropdown-item {
@apply block px-4 py-2 text-sm text-gray-700;
@apply hover:bg-gray-100 hover:text-gray-900;
@apply transition-colors duration-150;
}
.dropdown-divider {
@apply my-1 border-t border-gray-200;
}
/* مكون التلميح */
.tooltip {
@apply relative inline-block;
}
.tooltip-text {
@apply invisible absolute z-10 w-32 px-3 py-2;
@apply bg-gray-900 text-white text-sm text-center rounded-md;
@apply -top-12 left-1/2 -translate-x-1/2;
@apply opacity-0 transition-opacity duration-300;
}
.tooltip-text::after {
content: "";
@apply absolute top-full left-1/2 -translate-x-1/2;
border-width: 5px;
border-style: solid;
border-color: #1f2937 transparent transparent transparent;
}
.tooltip:hover .tooltip-text {
@apply visible opacity-100;
}
}
أفضل ممارسات استخراج المكونات
اتبع هذه المبادئ عند تقرير ما إذا كنت ستستخرج المكونات:
- الاستخدام الأول: اكتب الأدوات مباشرة في HTML
- الاستخدام الثاني: انسخ والصق، اتخذ ملاحظة ذهنية
- الاستخدام الثالث: فكر في الاستخراج إلى فئة @apply
لا تجرد بشكل مبكر—انتظر ظهور الأنماط الواضحة.
قابل للتركيب على الأحادي
جيد: نظام أزرار قابل للتركيب
/* فئات صغيرة قابلة للتركيب */
.btn {
@apply px-4 py-2 rounded-md font-medium;
}
.btn-solid {
@apply btn;
}
.btn-outline {
@apply btn border-2 bg-transparent;
}
/* الألوان كفئات منفصلة */
.btn-blue {
@apply bg-blue-500 text-white hover:bg-blue-600;
}
.btn-red {
@apply bg-red-500 text-white hover:bg-red-600;
}
/* الاستخدام: امزج وطابق */
<button class="btn btn-solid btn-blue">أزرق صلب</button>
<button class="btn btn-outline btn-red">أحمر محدد</button>
سيئ: فئات أزرار أحادية
/* لا تفعل: محدد جدًا، غير قابل للتركيب */
.btn-solid-blue {
@apply px-4 py-2 rounded-md font-medium;
@apply bg-blue-500 text-white hover:bg-blue-600;
}
.btn-solid-red {
@apply px-4 py-2 rounded-md font-medium;
@apply bg-red-500 text-white hover:bg-red-600;
}
.btn-outline-blue {
@apply px-4 py-2 rounded-md font-medium;
@apply border-2 border-blue-500 text-blue-500 bg-transparent;
}
/* عدد كبير جدًا من الفئات المحددة، الكثير من التكرار */
تحقيق التوازن بين الأدوات أولاً والمكونات
المفتاح هو إيجاد التوازن الصحيح بين فئات الأدوات وفئات المكونات:
مثال على النهج المتوازن
/* استخراج الأنماط الشائعة */
@layer components {
.card {
@apply bg-white rounded-lg shadow-md p-6;
}
.btn {
@apply px-4 py-2 rounded-md font-medium;
@apply focus:outline-none focus:ring-2 focus:ring-offset-2;
@apply transition-colors duration-200;
}
}
/* HTML: فئات مكونات + معدلات أدوات */
<div class="card border border-gray-200">
<h2 class="text-2xl font-bold mb-4">عنوان البطاقة</h2>
<p class="text-gray-600 mb-6">محتوى البطاقة يذهب هنا</p>
<button class="btn bg-blue-500 text-white hover:bg-blue-600">
إجراء
</button>
</div>
أنماط مكونات الإطار
في أطر JavaScript، فكر في الاستخراج القائم على المكونات بدلاً من فئات CSS:
مكون React (غالبًا أفضل من @apply)
// Button.jsx
function Button({ variant = 'primary', size = 'md', children }) {
const baseClasses = 'inline-flex items-center justify-center ' +
'rounded-md font-medium transition-colors ' +
'focus:outline-none focus:ring-2 focus:ring-offset-2';
const variantClasses = {
primary: 'bg-blue-500 text-white hover:bg-blue-600 focus:ring-blue-500',
secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300 focus:ring-gray-500',
danger: 'bg-red-500 text-white hover:bg-red-600 focus:ring-red-500'
};
const sizeClasses = {
sm: 'px-3 py-1.5 text-sm',
md: 'px-4 py-2 text-base',
lg: 'px-6 py-3 text-lg'
};
return (
<button
className={`${baseClasses} ${variantClasses[variant]} ${sizeClasses[size]}`}
>
{children}
</button>
);
}
// الاستخدام
<Button variant="primary" size="lg">اضغط هنا</Button>
<Button variant="danger">حذف</Button>
- استخدم @apply: لمواقع HTML الثابتة، القوالب، أو الأنماط العالمية حقًا
- استخدم المكونات: في React/Vue/Svelte للتركيب الأفضل والخصائص
مكتبة مكونات من العالم الحقيقي
إليك مكتبة مكونات عملية باستخدام @apply:
نظام مكونات كامل
@layer components {
/* الأزرار */
.btn {
@apply inline-flex items-center justify-center;
@apply px-4 py-2 border border-transparent rounded-md;
@apply font-medium text-sm transition-all duration-200;
@apply focus:outline-none focus:ring-2 focus:ring-offset-2;
@apply disabled:opacity-50 disabled:cursor-not-allowed;
}
.btn-primary { @apply btn bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500; }
.btn-secondary { @apply btn bg-gray-200 text-gray-900 hover:bg-gray-300 focus:ring-gray-500; }
.btn-success { @apply btn bg-green-600 text-white hover:bg-green-700 focus:ring-green-500; }
.btn-danger { @apply btn bg-red-600 text-white hover:bg-red-700 focus:ring-red-500; }
/* الشارات */
.badge {
@apply inline-flex items-center px-2.5 py-0.5 rounded-full;
@apply text-xs font-medium;
}
.badge-primary { @apply badge bg-blue-100 text-blue-800; }
.badge-success { @apply badge bg-green-100 text-green-800; }
.badge-warning { @apply badge bg-yellow-100 text-yellow-800; }
.badge-danger { @apply badge bg-red-100 text-red-800; }
/* التنبيهات */
.alert {
@apply p-4 rounded-md border;
}
.alert-info { @apply alert bg-blue-50 border-blue-200 text-blue-800; }
.alert-success { @apply alert bg-green-50 border-green-200 text-green-800; }
.alert-warning { @apply alert bg-yellow-50 border-yellow-200 text-yellow-800; }
.alert-error { @apply alert bg-red-50 border-red-200 text-red-800; }
/* عناصر النموذج */
.form-input {
@apply w-full px-3 py-2 border border-gray-300 rounded-md;
@apply placeholder-gray-400 focus:outline-none;
@apply focus:ring-2 focus:ring-blue-500 focus:border-transparent;
@apply transition-all duration-200;
}
.form-label {
@apply block text-sm font-medium text-gray-700 mb-1;
}
.form-error {
@apply text-sm text-red-600 mt-1;
}
/* الروابط */
.link {
@apply text-blue-600 hover:text-blue-800 underline;
@apply transition-colors duration-200;
}
.link-muted {
@apply text-gray-600 hover:text-gray-900 no-underline;
}
}
تمرين تطبيقي
المهمة: أنشئ نظام مكونات الإشعارات باستخدام @apply:
- أنشئ فئة
.notificationأساسية مع padding، زوايا مستديرة، حدود، وظل - أضف متغيرات:
.notification-info،.notification-success،.notification-warning،.notification-error - أنشئ المكونات الفرعية
.notification-titleو.notification-body - أضف متغير
.notification-dismissableمع زر إغلاق - ابنِ أمثلة HTML تعرض جميع المتغيرات
- استخدم توجيه @layer لتنظيم الكود الخاص بك بشكل صحيح
تمرين التحدي
المهمة المتقدمة: ابنِ نظام مكونات بطاقة تسعير كامل:
- أنشئ
.pricing-cardأساسية مع أقسام header، body، و footer - أضف متغير
.pricing-card-featuredمع تصميم بارز - أنشئ المكونات الفرعية:
.pricing-header،.pricing-price،.pricing-features،.pricing-cta - أضف تأثيرات hover والانتقالات
- اجعلها متجاوبة مع تخطيطات مختلفة للجوال/سطح المكتب
- ابنِ صفحة تسعير من 3 مستويات (أساسي، محترف، مؤسسة) باستخدام مكوناتك
- وازن بين فئات @apply والأدوات المضمنة للمرونة
الملخص
في هذا الدرس، تعلمت:
- ما الذي يفعله
@applyوكيفية استخدامه بفعالية - متى تستخدم @apply (الأنماط المتكررة، أنظمة المكونات)
- متى لا تستخدم @apply (مرات واحدة، تركيبات بسيطة، مكونات الإطار)
- توجيه
@layerلتنظيم الأنماط المخصصة - أفضل الممارسات لاستخراج المكونات (قاعدة الثلاثة)
- تحقيق التوازن بين نهج الأدوات أولاً وفئات المكونات
- التصميم القابل للتركيب مقابل الأحادي للمكونات
- الأنماط الخاصة بالإطار (مكونات React/Vue مقابل فئات CSS)
المفتاح لإتقان @apply هو ضبط النفس—استخدمه عندما يحسن حقًا قابلية الصيانة، وليس فقط لأنك تستطيع. في الدرس التالي، سنستكشف تنفيذ الوضع الداكن في Tailwind.