كيفية تنسيق تطبيق React: 5 طرق عملية لكتابة CSS باحتراف

دقائق القراءة: 9
طرق تنسيق تطبيق React باستخدام CSS وSCSS وCSS Modules وCSS-in-JS

عند البدء في تنسيق تطبيقات React، ستجد أمامك خيارات متعددة، ولكل خيار مزاياه وقيوده. ولا توجد طريقة واحدة مثالية تناسب جميع المشاريع، لأن اختيار أسلوب كتابة CSS يعتمد على حجم التطبيق، وهيكل الفريق، وقابلية التوسع المطلوبة، وطبيعة المكونات التي تعمل عليها.

في هذا الدليل، سنستعرض خمس طرق شائعة لتنسيق تطبيقات React، مع توضيح الفروقات بينها بصورة عملية، حتى تتمكن من اختيار الأسلوب الأنسب لمشروعك الحالي أو القادم.

ما الذي سنبنيه في الأمثلة؟

لمقارنة هذه الأساليب بشكل واضح، سنفترض أننا نبني المكوّن نفسه في كل مرة: بطاقة Testimonial بسيطة وأنيقة لعرض رأي مستخدم أو عميل.

مثال بطاقة آراء العملاء داخل تطبيق React

وإذا أردت تجربة هذه الأمثلة بنفسك، يمكنك إنشاء مشروع جديد بسرعة عبر بيئة react.new.

1. استخدام التنسيقات المضمنة Inline Styles

تُعد Inline Styles من أسرع الطرق لتطبيق التنسيق مباشرة داخل عناصر JSX. في هذا الأسلوب، لا تحتاج إلى ملف أنماط منفصل، لأنك تضيف خاصية style مباشرة إلى العنصر.

كما أن هذا النوع من التنسيق يمتلك أولوية أعلى من كثير من القواعد الموجودة في ملفات الأنماط التقليدية، ما يجعله مناسباً في بعض الحالات السريعة أو المؤقتة.

مثال على استخدام Inline Styles

export default function App() {
  return (
    <section
      style={{
        fontFamily: '-apple-system',
        fontSize: '1rem',
        fontWeight: 1.5,
        lineHeight: 1.5,
        color: '#292b2c',
        backgroundColor: '#fff',
        padding: '0 2em'
      }}
    >
      <div
        style={{
          textAlign: 'center',
          maxWidth: '950px',
          margin: '0 auto',
          border: '1px solid #e6e6e6',
          padding: '40px 25px',
          marginTop: '50px'
        }}
      >
        <img
          src="https://randomuser.me/api/portraits/women/48.jpg"
          alt="Tammy Stevens"
          style={{
            margin: '-90px auto 30px',
            width: '100px',
            borderRadius: '50%',
            objectFit: 'cover',
            marginBottom: '0'
          }}
        />
        <div>
          <p
            style={{
              lineHeight: 1.5,
              fontWeight: 300,
              marginBottom: '25px',
              fontSize: '1.375rem'
            }}
          >
            This is one of the best developer blogs on the planet! I read it daily to improve my skills.
          </p>
        </div>
        <p style={{ marginBottom: '0', fontWeight: 600, fontSize: '1rem' }}>
          Tammy Stevens <span style={{ fontWeight: 400 }}>· Front End Developer</span>
        </p>
      </div>
    </section>
  );
}

المشكلة الأساسية هنا أن الكود يصبح مزدحماً سريعاً، حتى في مكوّن بسيط نسبياً. ومع توسع المشروع، ستلاحظ أن قراءة ملفات JSX وصيانتها تصبح أكثر صعوبة.

تحسين الفكرة باستخدام كائن أنماط قابل لإعادة الاستخدام

const styles = {
  section: {
    fontFamily: '-apple-system',
    fontSize: '1rem',
    fontWeight: 1.5,
    lineHeight: 1.5,
    color: '#292b2c',
    backgroundColor: '#fff',
    padding: '0 2em'
  },
  wrapper: {
    textAlign: 'center',
    maxWidth: '950px',
    margin: '0 auto',
    border: '1px solid #e6e6e6',
    padding: '40px 25px',
    marginTop: '50px'
  },
  avatar: {
    margin: '-90px auto 30px',
    width: '100px',
    borderRadius: '50%',
    objectFit: 'cover',
    marginBottom: '0'
  },
  quote: {
    lineHeight: 1.5,
    fontWeight: 300,
    marginBottom: '25px',
    fontSize: '1.375rem'
  },
  name: {
    marginBottom: '0',
    fontWeight: 600,
    fontSize: '1rem'
  },
  position: {
    fontWeight: 400
  }
};

export default function App() {
  return (
    <section style={styles.section}>
      <div style={styles.wrapper}>
        <img
          src="https://randomuser.me/api/portraits/women/48.jpg"
          alt="Tammy Stevens"
          style={styles.avatar}
        />
        <div>
          <p style={styles.quote}>
            This is one of the best developer blogs on the planet! I read it daily to improve my skills.
          </p>
        </div>
        <p style={styles.name}>
          Tammy Stevens <span style={styles.position}>· Front End Developer</span>
        </p>
      </div>
    </section>
  );
}

هذا الأسلوب أفضل من وضع جميع القواعد مباشرة داخل العناصر، لكنه لا يحل جميع المشكلات. فالتنسيقات المضمنة لا تدعم كثيراً من مزايا CSS المهمة مثل:

  • الحركات animations
  • المحددات المتقدمة selectors
  • الحالات الوصفية مثل :hover
  • العناصر الوهمية مثل ::before و::first-line

متى تكون مناسبة؟

تُعد مناسبة في النماذج الأولية السريعة prototyping أو في المكونات الصغيرة جداً، لكن يصعب الاعتماد عليها في التطبيقات المتوسطة والكبيرة.

المزايا والعيوب

  • المزايا:
    • أسرع طريقة لكتابة تنسيقات بسيطة
    • مفيدة للتجارب الأولية
    • تمتلك أولوية عالية عند تطبيق الأنماط
  • العيوب:
    • تحويل CSS التقليدي إليها مرهق
    • تجعل ملفات JSX أقل وضوحاً
    • لا تدعم كثيراً من خصائص CSS الأساسية
    • ضعيفة من ناحية التوسع والصيانة

2. استخدام CSS التقليدي

الطريقة الأشهر والأبسط هي إنشاء ملف CSS منفصل ثم استيراده داخل المكوّن أو التطبيق. وعلى الرغم من أن هذا الأسلوب قديم نسبياً، فإنه ما زال عملياً جداً، خصوصاً مع التطور المستمر في معيار CSS نفسه.

اليوم بات CSS يوفر مزايا متقدمة مثل:

  • المتغيرات CSS Variables
  • المحددات الحديثة
  • الحالات الجديدة مثل :is و:where

مثال على استخدام ملف CSS عادي

/* src/styles.css */
body {
  font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  margin: 0;
  font-size: 1rem;
  font-weight: 1.5;
  line-height: 1.5;
  color: #292b2c;
  background-color: #fff;
}

.testimonial {
  margin: 0 auto;
  padding: 0 2em;
}

.testimonial-wrapper {
  text-align: center;
  max-width: 950px;
  margin: 0 auto;
  border: 1px solid #e6e6e6;
  padding: 40px 25px;
  margin-top: 50px;
}

.testimonial-quote p {
  line-height: 1.5;
  font-weight: 300;
  margin-bottom: 25px;
  font-size: 1.375rem;
}

.testimonial-avatar {
  margin: -90px auto 30px;
  width: 100px;
  border-radius: 50%;
  object-fit: cover;
  margin-bottom: 0;
}

.testimonial-name {
  margin-bottom: 0;
  font-weight: 600;
  font-size: 1rem;
}

.testimonial-name span {
  font-weight: 400;
}
import "./styles.css";

export default function App() {
  return (
    <section className="testimonial">
      <div className="testimonial-wrapper">
        <img
          className="testimonial-avatar"
          src="https://randomuser.me/api/portraits/women/48.jpg"
          alt="Tammy Stevens"
        />
        <div className="testimonial-quote">
          <p>
            This is one of the best developer blogs on the planet! I read it daily to improve my skills.
          </p>
        </div>
        <p className="testimonial-name">
          Tammy Stevens <span>· Front End Developer</span>
        </p>
      </div>
    </section>
  );
}

لاحظ استخدام بادئة مشتركة في أسماء الأصناف مثل testimonial- لتقليل التعارض وتسهيل التنظيم. هذا الأسلوب فعال، لكن مع كبر المشروع تبدأ تحديات التسمية والاعتماد على النطاق العام global scope بالظهور.

أبرز التحديات

  • إمكانية تعارض أسماء الأصناف class names
  • تأثير الأنماط على العناصر الأبناء بشكل متسلسل cascade
  • الحاجة إلى أسلوب تسمية منظم مثل BEM
  • الاعتماد على أدوات إضافية لدعم بعض المتصفحات مثل vendor prefixing

المزايا والعيوب

  • المزايا:
    • يوفر كامل قدرات CSS الحديث
    • يفصل الأنماط عن المكونات ويحسن قابلية القراءة
    • مناسب للمطورين المتمكنين من CSS التقليدي
  • العيوب:
    • غير معزول على مستوى المكوّنات
    • قد يتطلب كتابة أكثر من الأدوات الحديثة
    • الحفاظ على أسماء فريدة يصبح أصعب مع الوقت

3. استخدام SASS وSCSS

SASS هو اختصار لعبارة Syntactically Awesome Style Sheets، وهو معالج أنماط يضيف مزايا قوية فوق CSS التقليدي. من أبرز هذه المزايا:

  • المتغيرات
  • الوراثة عبر @extend
  • التداخل nesting
  • المزائج mixins

استخدام SCSS لإضافة التداخل والمتغيرات في تنسيق React

يمكن كتابة SASS بصيغتين: .scss و.sass. الصيغة الأولى أقرب إلى CSS التقليدي، لذلك فهي الأكثر شيوعاً بين المطورين.

مثال سريع على التداخل في SCSS

/* styles.scss */
nav {
  ul {
    margin: 0;
    padding: 0;
    list-style: none;
  }

  li {
    display: inline-block;
  }

  a {
    display: block;
    padding: 6px 12px;
    text-decoration: none;
  }
}

الصيغة المقابلة في SASS

/* styles.sass */
nav
  ul
    margin: 0
    padding: 0
    list-style: none
  li
    display: inline-block
  a
    display: block
    padding: 6px 12px
    text-decoration: none

ولأن هذه الصيغة ليست CSS خاماً، فهي تحتاج إلى عملية تحويل compile قبل الاستخدام داخل المشروع.

تثبيت node-sass

npm install node-sass

مثال على بطاقة Testimonial باستخدام SCSS

/* src/styles.scss */
$font-stack: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
$text-color: #292b2c;

%font-basic {
  font-size: 1rem;
}

body {
  @extend %font-basic;
  font-family: $font-stack;
  color: $text-color;
  margin: 0;
  font-size: 1rem;
  font-weight: 1.5;
  line-height: 1.5;
  background-color: #fff;
}

.testimonial-name {
  @extend %font-basic;
  margin-bottom: 0;
  font-weight: 600;

  span {
    font-weight: 400;
  }
}

في المثال السابق نلاحظ ثلاث مزايا مهمة:

  1. المتغيرات: مثل $font-stack و$text-color.
  2. الوراثة: عبر المحدد %font-basic مع @extend.
  3. التداخل: مثل كتابة span داخل .testimonial-name.

المزايا والعيوب

  • المزايا:
    • تقليل التكرار ورفع إنتاجية كتابة الأنماط
    • إضافة مزايا برمجية عملية فوق CSS
    • تنظيم أفضل عند العمل على واجهات كبيرة
  • العيوب:
    • يبقى النطاق عاماً مثل CSS العادي
    • قد يتطلب إعداداً إضافياً في بعض المشاريع
    • بعض مزاياه أصبحت متاحة جزئياً في CSS الحديث

4. استخدام CSS Modules

تُعد CSS Modules حلاً وسطاً ممتازاً بين سهولة CSS التقليدي ومشكلة التداخل بين الأصناف. الفكرة بسيطة: تكتب أنماطك بالطريقة المعتادة، لكن يتم توليد أسماء أصناف فريدة تلقائياً عند بناء المشروع.

وهذا يعني أن النمط يصبح معزولاً داخل المكوّن الذي يستورده، بدلاً من أن ينتشر عالمياً داخل التطبيق.

مثال على CSS Modules

/* src/styles.module.css */
body {
  font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  margin: 0;
  font-size: 1rem;
  font-weight: 1.5;
  line-height: 1.5;
  color: #292b2c;
  background-color: #fff;
}

.testimonial-name span {
  font-weight: 400;
}
import styles from './styles.module.css';

export default function App() {
  return (
    <section className={styles.testimonial}>
      <div className={styles['testimonial-wrapper']}>
        <img
          src="https://randomuser.me/api/portraits/women/48.jpg"
          alt="Tammy Stevens"
          className={styles['testimonial-avatar']}
        />
        <div>
          <p className={styles['testimonial-quote']}>
            This is one of the best developer blogs on the planet! I read it daily to improve my skills.
          </p>
        </div>
        <p className={styles['testimonial-name']}>
          Tammy Stevens <span>· Front End Developer</span>
        </p>
      </div>
    </section>
  );
}

يشترط هذا الأسلوب أن يحتوي اسم الملف على .module قبل الامتداد، مثل styles.module.css أو styles.module.scss.

كيف يبدو الناتج بعد البناء؟

<p class="_styles__testimonial-name_309571057">Tammy Stevens</p>

بهذا الشكل، يتم منع تعارض الأسماء تلقائياً، كما تصبح الأنماط أكثر قابلية للتوقع والصيانة، خاصة في المشاريع الكبيرة أو في فرق العمل المتعددة.

المزايا والعيوب

  • المزايا:
    • عزل الأنماط على مستوى المكوّن
    • منع التعارض بفضل الأسماء المولدة تلقائياً
    • البدء بها سهل جداً في مشاريع Create React App
    • يمكن استخدامها مع CSS أو SASS
  • العيوب:
    • قد تبدو طريقة الإشارة إلى الأصناف أقل مباشرة
    • تحتاج إلى وقت بسيط للتعود على نمط الاستيراد والاستخدام

5. استخدام CSS-in-JS

يأخذ أسلوب CSS-in-JS فكرة الدمج بين المنطق والواجهة إلى مستوى أعمق. فبدلاً من كتابة الأنماط في ملف .css منفصل، تكتبها داخل ملفات JavaScript نفسها، وغالباً داخل المكوّن أو بجواره مباشرة.

من أشهر المكتبات في هذا المجال: Styled Components وEmotion.

مثال أساسي باستخدام Styled Components

import styled from "styled-components";

const Button = styled.button`
  color: limegreen;
  border: 2px solid limegreen;
  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border-radius: 3px;

  &:hover {
    opacity: 0.9;
  }
`;

export default function App() {
  return (
    <div>
      <Button>Click me</Button>
    </div>
  );
}

مثال زر منسق باستخدام Styled Components داخل React

الميزة الجميلة هنا أنك ما زلت تكتب قواعد شبيهة بـCSS العادي، لكنك تستفيد في الوقت نفسه من مرونة JavaScript والمكوّنات.

إضافة أنماط ديناميكية عبر props

import styled from "styled-components";

const Button = styled.button`
  background: ${props => (props.inverted ? "limegreen" : "white")};
  color: ${props => (props.inverted ? "white" : "limegreen")};
  border: 2px solid limegreen;
  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border-radius: 3px;

  &:hover {
    opacity: 0.9;
  }
`;

export default function App() {
  return (
    <div>
      <Button>Click me</Button>
      <Button inverted>Click me</Button>
    </div>
  );
}

في هذا المثال، يتم استخدام الخاصية inverted لتغيير لون الخلفية والنص بشكل ديناميكي. وهذا يمنحك مرونة عالية في بناء مكوّنات قابلة لإعادة الاستخدام مع اختلافات متعددة دون تكرار.

أزرار ديناميكية في React باستخدام props وStyled Components

لماذا يفضله كثير من المطورين؟

  • الأنماط معزولة لكل مكوّن
  • إمكانية بناء تنسيقات ديناميكية اعتماداً على props
  • قابلية عالية لإعادة الاستخدام والتوسعة
  • عدم الحاجة إلى التفكير المستمر في أسماء الأصناف

المزايا والعيوب

  • المزايا:
    • تنسيقات متوقعة ومعزولة على مستوى المكوّن
    • سهولة إعادة الاستخدام والتوسعة
    • دعم قوي للتنظيم الديناميكي والشرطي
    • تقليل مشكلات تعارض الأنماط
  • العيوب:
    • الحاجة إلى مكتبات خارجية إضافية
    • زيادة بسيطة في حجم المشروع النهائي
    • قد لا يكون الخيار المفضل لمن يحب الفصل الصارم بين الهيكل والتنسيق

مقارنة سريعة بين الأساليب الخمسة

الأسلوب سهولة البداية العزل القابلية للتوسع الحالات المناسبة
Inline Styles مرتفعة جزئي ضعيفة النماذج الأولية والمكونات الصغيرة
CSS التقليدي مرتفعة لا متوسطة المشاريع البسيطة أو الفرق المعتادة على CSS
SASS/SCSS متوسطة لا جيدة المشاريع التي تحتاج تنظيماً ومرونة أكبر
CSS Modules مرتفعة نعم مرتفعة التطبيقات المتوسطة والكبيرة
CSS-in-JS متوسطة نعم مرتفعة جداً المكونات الديناميكية والواجهات الحديثة

كيف تختار الطريقة الأنسب لمشروعك؟

  • إذا كنت تبني نموذجاً أولياً سريعاً، فاختر Inline Styles.
  • إذا أردت حلاً تقليدياً واضحاً وسهل الفهم، فابدأ مع CSS العادي.
  • إذا كنت تحتاج أدوات أكثر قوة مع صياغة مريحة، فاستخدم SCSS.
  • إذا كان هدفك هو عزل الأنماط دون تعقيد كبير، فـCSS Modules خيار ممتاز.
  • إذا كنت تريد أنماطاً ديناميكية شديدة الارتباط بالمكوّنات، فجرب CSS-in-JS.

ملاحظة مهمة حول مكتبات الواجهات الجاهزة

هذه المقارنة تركز على طرق كتابة الأنماط يدوياً داخل تطبيقات React. لكن هذا لا يعني أن استخدام مكتبات واجهات جاهزة مثل Material UI أو Ant Design خيار غير مناسب. على العكس، قد تكون هذه المكتبات حلاً ممتازاً إذا كان مشروعك يحتاج إلى سرعة في التطوير وتوحيد بصري جاهز.

الخلاصة التقنية

من الناحية العملية، لا توجد طريقة واحدة يمكن وصفها بأنها الأفضل دائماً في تنسيق تطبيقات React. إذا كان مشروعك صغيراً، فقد يكفيك CSS التقليدي أو SCSS. أما إذا كنت تعمل على تطبيق قابل للتوسع وتريد تقليل التعارض ورفع قابلية الصيانة، فغالباً ستكون CSS Modules أو CSS-in-JS خيارات أكثر نضجاً. القرار الصحيح هو الذي يوازن بين سرعة التطوير، وسهولة الصيانة، وخبرة الفريق، وطبيعة الواجهة التي تبنيها.

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *