معالج SASS/SCSS

توجيهات التحكم: @if و @else if و @else

20 دقيقة الدرس 14 من 30

المنطق الشرطي في SASS

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

صيغة @if

يقيّم توجيه @if شرطاً ويتضمن كتلة من الأنماط فقط إذا كان الشرط صحيحاً. الصيغة بسيطة ومشابهة لعبارات الشرط في لغات البرمجة الأخرى.

مثال أساسي على @if

$theme: dark;

.header {
  @if $theme == dark {
    background-color: #333;
    color: #fff;
  }
}

// CSS المجمع:
// .header {
//   background-color: #333;
//   color: #fff;
// }

// إذا كان $theme هو 'light'، لن يتم إنشاء أنماط لـ .header

// استخدام @if في قيم الخصائص
$enable-shadows: true;

.card {
  padding: 20px;
  border-radius: 8px;

  @if $enable-shadows {
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  }
}

// مع تمكين الظلال:
// .card {
//   padding: 20px;
//   border-radius: 8px;
//   box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
// }
ملاحظة: يقيّم توجيه @if الشرط في وقت التجميع، وليس في وقت التشغيل. هذا يعني أن القرار يُتخذ عندما يتم تجميع SASS إلى CSS، وليس عندما يتم تحميل CSS في المتصفح. هذا مختلف بشكل أساسي عن الشروط في JavaScript.

@else if و @else

يمكنك توسيع عبارات @if بـ @else if لاختبار شروط متعددة، و @else لتوفير احتياطي عندما لا تُستوفى أي شروط.

سلسلة شرطية كاملة

$size: medium;

.button {
  @if $size == small {
    padding: 8px 16px;
    font-size: 14px;
  } @else if $size == medium {
    padding: 12px 24px;
    font-size: 16px;
  } @else if $size == large {
    padding: 16px 32px;
    font-size: 18px;
  } @else {
    // احتياطي افتراضي
    padding: 10px 20px;
    font-size: 15px;
  }
}

// CSS المجمع (مع $size: medium):
// .button {
//   padding: 12px 24px;
//   font-size: 16px;
// }

// شروط متعددة مع خصائص مختلفة
$layout: grid;
$columns: 3;

.container {
  display: flex;

  @if $layout == grid {
    display: grid;

    @if $columns == 2 {
      grid-template-columns: repeat(2, 1fr);
    } @else if $columns == 3 {
      grid-template-columns: repeat(3, 1fr);
    } @else if $columns == 4 {
      grid-template-columns: repeat(4, 1fr);
    } @else {
      grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
    }
  } @else if $layout == flex {
    display: flex;
    flex-wrap: wrap;
    gap: 20px;
  }
}

// CSS المجمع:
// .container {
//   display: flex;
//   display: grid;
//   grid-template-columns: repeat(3, 1fr);
// }

الصحة والزيف في SASS

فهم ما تعتبره SASS "صحيحاً" و "زائفاً" أمر حاسم لكتابة منطق شرطي صحيح. في SASS، يُعتبر قيمتان فقط زائفتين: false و null. كل شيء آخر، بما في ذلك السلاسل النصية الفارغة والصفر والقوائم الفارغة، يُعتبر صحيحاً.

القيم الصحيحة والزائفة

// قيم زائفة
$value1: false;
$value2: null;

// قيم صحيحة (كل شيء آخر!)
$value3: true;
$value4: 0;          // صحيح بشكل مفاجئ!
$value5: "";         // أيضاً صحيح!
$value6: ();         // القائمة الفارغة صحيحة!
$value7: #000;       // أي لون صحيح

// أمثلة
.test-1 {
  @if $value1 {
    color: red;  // غير مضمنة (false)
  }
}

.test-2 {
  @if $value2 {
    color: blue; // غير مضمنة (null)
  }
}

.test-3 {
  @if $value4 {
    color: green; // مضمنة! (0 صحيح)
  }
}

// CSS المجمع:
// .test-3 {
//   color: green;
// }

// التحقق من null مقابل false
$border-width: null;

.element {
  @if $border-width {
    border: $border-width solid #ccc;
  } @else {
    border: none;
  }
}

// النتيجة: border: none;

// كن حذراً مع الصفر!
$margin: 0;

.container {
  @if $margin {
    margin: $margin; // هذا سيتم تضمينه!
  }
}

// النتيجة: margin: 0;
تحذير: الرقم 0 صحيح في SASS، وهو مختلف عن العديد من لغات البرمجة. إذا كنت بحاجة إلى التحقق مما إذا كان الرقم صفراً، استخدم مقارنة صريحة: @if $value != 0 بدلاً من @if $value.

عوامل المقارنة

تدعم SASS جميع عوامل المقارنة القياسية لبناء شروط معقدة.

جميع عوامل المقارنة

// == (يساوي)
$color: blue;

.equal-test {
  @if $color == blue {
    background: $color; // مضمنة
  }
}

// != (لا يساوي)
$theme: light;

.not-equal-test {
  @if $theme != dark {
    background: white; // مضمنة
  }
}

// < (أقل من)
$width: 500px;

.less-than-test {
  @if $width < 600px {
    max-width: 100%; // مضمنة
  }
}

// > (أكبر من)
$font-size: 18px;

.greater-than-test {
  @if $font-size > 16px {
    line-height: 1.6; // مضمنة
  }
}

// <= (أقل من أو يساوي)
$count: 5;

.less-equal-test {
  @if $count <= 5 {
    display: grid; // مضمنة
  }
}

// >= (أكبر من أو يساوي)
$priority: 10;

.greater-equal-test {
  @if $priority >= 10 {
    z-index: 1000; // مضمنة
  }
}

// مقارنة السلاسل النصية
$alignment: center;

.string-compare {
  @if $alignment == center {
    text-align: center; // مضمنة
  }
}

// مقارنة الألوان
$bg: #fff;

.color-compare {
  @if $bg == white {
    color: black; // مضمنة (أسماء الألوان تتحلل إلى hex)
  }
}

العوامل المنطقية

توفر SASS عوامل منطقية لدمج شروط متعددة: and و or و not.

عامل AND

// and - يجب أن يكون كلا الشرطين صحيحاً
$is-premium: true;
$is-active: true;

.user-badge {
  @if $is-premium and $is-active {
    background: gold;
    color: white;
  }
}

// مجمع: background: gold; color: white;

// شروط AND متعددة
$screen-size: 1024px;
$orientation: landscape;
$high-res: true;

.responsive-layout {
  @if $screen-size >= 1024px and $orientation == landscape and $high-res {
    // يجب أن تكون الشروط الثلاثة صحيحة
    background-image: url('hero-large@2x.jpg');
  }
}

عامل OR

// or - يجب أن يكون شرط واحد على الأقل صحيحاً
$device: mobile;

.menu {
  @if $device == mobile or $device == tablet {
    display: none; // مخفية على الجوال أو الجهاز اللوحي
  }
}

// تسلسل شروط OR
$role: editor;

.admin-panel {
  @if $role == admin or $role == editor or $role == moderator {
    display: block;
  } @else {
    display: none;
  }
}

// دمج AND و OR (استخدم الأقواس للوضوح)
$user-level: 5;
$is-verified: true;
$is-subscriber: false;

.premium-content {
  @if ($user-level >= 5 and $is-verified) or $is-subscriber {
    opacity: 1;
    pointer-events: auto;
  } @else {
    opacity: 0.5;
    pointer-events: none;
  }
}

عامل NOT

// not - ينفي الشرط
$dark-mode: false;

.content {
  @if not $dark-mode {
    background: white;
    color: black;
  }
}

// NOT مع المقارنات
$width: 300px;

.container {
  @if not ($width > 500px) {
    // مكافئ لـ: @if $width <= 500px
    max-width: 100%;
  }
}

// NOT مع فحص null
$custom-font: null;

.text {
  @if not $custom-font {
    font-family: Arial, sans-serif; // استخدم الافتراضي
  } @else {
    font-family: $custom-font;
  }
}

// تعبيرات منطقية معقدة
$has-errors: true;
$is-loading: false;
$is-empty: false;

.form-state {
  @if not $has-errors and not $is-loading and not $is-empty {
    border-color: green; // حالة نجاح
  }
}
نصيحة: عند دمج عوامل منطقية متعددة، استخدم الأقواس لتوضيح نواياك، حتى عندما لا تكون ضرورية بشكل صارم. هذا يحسن القراءة ويمنع الأخطاء المنطقية: @if ($a and $b) or ($c and $d)

استخدام @if في الـ Mixins للمخرجات الشرطية

واحد من أقوى تطبيقات @if هو إنشاء mixins التي تولد CSS مختلفاً بشكل مشروط بناءً على المعاملات.

Mixin استجابي مع الشروط

// mixin نقطة التوقف مع @if
@mixin respond-to($breakpoint) {
  @if $breakpoint == phone {
    @media (max-width: 599px) {
      @content;
    }
  } @else if $breakpoint == tablet {
    @media (min-width: 600px) and (max-width: 1023px) {
      @content;
    }
  } @else if $breakpoint == desktop {
    @media (min-width: 1024px) {
      @content;
    }
  } @else if $breakpoint == large-desktop {
    @media (min-width: 1440px) {
      @content;
    }
  } @else {
    @warn "نقطة التوقف '#{$breakpoint}' غير معروفة.";
  }
}

// الاستخدام
.sidebar {
  width: 100%;

  @include respond-to(tablet) {
    width: 300px;
  }

  @include respond-to(desktop) {
    width: 350px;
  }
}

// mixin متغير الزر
@mixin button-variant($style: solid, $color: blue) {
  @if $style == solid {
    background-color: $color;
    color: white;
    border: 2px solid $color;

    &:hover {
      background-color: darken($color, 10%);
    }
  } @else if $style == outline {
    background-color: transparent;
    color: $color;
    border: 2px solid $color;

    &:hover {
      background-color: $color;
      color: white;
    }
  } @else if $style == ghost {
    background-color: transparent;
    color: $color;
    border: none;

    &:hover {
      background-color: rgba($color, 0.1);
    }
  }
}

.btn-primary {
  @include button-variant(solid, #007bff);
}

.btn-secondary {
  @include button-variant(outline, #6c757d);
}

.btn-link {
  @include button-variant(ghost, #007bff);
}

Mixin التباعد مع التحكم في الاتجاه

@mixin spacing($size: md, $type: margin, $direction: all) {
  $sizes: (
    xs: 4px,
    sm: 8px,
    md: 16px,
    lg: 24px,
    xl: 32px
  );

  $value: map-get($sizes, $size);

  @if not $value {
    @error "الحجم '#{$size}' غير موجود. استخدم: xs، sm، md، lg، xl";
  }

  @if $direction == all {
    #{$type}: $value;
  } @else if $direction == vertical {
    #{$type}-top: $value;
    #{$type}-bottom: $value;
  } @else if $direction == horizontal {
    #{$type}-left: $value;
    #{$type}-right: $value;
  } @else if $direction == top {
    #{$type}-top: $value;
  } @else if $direction == bottom {
    #{$type}-bottom: $value;
  } @else if $direction == left {
    #{$type}-left: $value;
  } @else if $direction == right {
    #{$type}-right: $value;
  } @else {
    @warn "الاتجاه '#{$direction}' غير معروف.";
  }
}

// الاستخدام
.card {
  @include spacing(md, padding, all);
  // النتيجة: padding: 16px;
}

.header {
  @include spacing(lg, margin, vertical);
  // النتيجة: margin-top: 24px; margin-bottom: 24px;
}

.sidebar {
  @include spacing(xl, padding, right);
  // النتيجة: padding-right: 32px;
}

استخدام @if في الدوال للقيم المُرجعة الشرطية

الدوال المدمجة مع @if تسمح لك بإرجاع قيم مختلفة بناءً على الشروط، مما ينشئ خدمات مساعدة ذكية.

إرجاع شرطي في الدوال

// تحديد لون النص بناءً على سطوع الخلفية
@function text-color($bg-color, $threshold: 60%) {
  @if lightness($bg-color) > $threshold {
    @return #000; // نص داكن للخلفيات الفاتحة
  } @else {
    @return #fff; // نص فاتح للخلفيات الداكنة
  }
}

.button-light {
  $bg: #f0f0f0;
  background-color: $bg;
  color: text-color($bg);
  // النتيجة: color: #000;
}

.button-dark {
  $bg: #2c3e50;
  background-color: $bg;
  color: text-color($bg);
  // النتيجة: color: #fff;
}

// الحصول على قيمة مع احتياطي
@function get-theme-value($key, $fallback: null) {
  $theme: (
    primary: #007bff,
    secondary: #6c757d,
    success: #28a745
  );

  @if map-has-key($theme, $key) {
    @return map-get($theme, $key);
  } @else if $fallback {
    @warn "مفتاح الموضوع '#{$key}' غير موجود. استخدام الاحتياطي.";
    @return $fallback;
  } @else {
    @error "مفتاح الموضوع '#{$key}' غير موجود ولا يوجد احتياطي.";
  }
}

.element {
  color: get-theme-value(primary);
  // النتيجة: color: #007bff;
}

.fallback-element {
  color: get-theme-value(unknown, #999);
  // تحذير صادر، النتيجة: color: #999;
}

// حساب حجم استجابي
@function responsive-size($min, $max, $viewport-width) {
  @if $viewport-width < 768px {
    @return $min;
  } @else if $viewport-width >= 768px and $viewport-width < 1200px {
    // استيفاء خطي بين الحد الأدنى والأقصى
    $progress: ($viewport-width - 768px) / (1200px - 768px);
    @return $min + ($max - $min) * $progress;
  } @else {
    @return $max;
  }
}

// تحويل الوحدة مع التحقق
@function to-rem($value) {
  @if type-of($value) != number {
    @error "يجب أن تكون القيمة رقماً. حصلت على: #{type-of($value)}";
  }

  @if unitless($value) {
    @warn "القيمة ليس لها وحدة. افتراض بكسلات.";
    $value: $value * 1px;
  }

  @if unit($value) == "rem" {
    @return $value;
  } @else if unit($value) == "px" {
    @return ($value / 16px) * 1rem;
  } @else {
    @error "لا يمكن تحويل #{unit($value)} إلى rem.";
  }
}

أمثلة من العالم الواقعي

نظام تبديل الموضوع

// تكوين الموضوع
$current-theme: dark;

$themes: (
  light: (
    bg-primary: #ffffff,
    bg-secondary: #f8f9fa,
    text-primary: #212529,
    text-secondary: #6c757d,
    border-color: #dee2e6
  ),
  dark: (
    bg-primary: #212529,
    bg-secondary: #343a40,
    text-primary: #f8f9fa,
    text-secondary: #adb5bd,
    border-color: #495057
  )
);

// الحصول على قيمة الموضوع
@function theme($key) {
  $theme-map: map-get($themes, $current-theme);

  @if not $theme-map {
    @error "الموضوع '#{$current-theme}' غير موجود.";
  }

  @if map-has-key($theme-map, $key) {
    @return map-get($theme-map, $key);
  } @else {
    @error "المفتاح '#{$key}' غير موجود في الموضوع '#{$current-theme}'.";
  }
}

// تطبيق الموضوع
body {
  background-color: theme(bg-primary);
  color: theme(text-primary);
}

.card {
  background-color: theme(bg-secondary);
  border: 1px solid theme(border-color);
  color: theme(text-primary);
}

.muted-text {
  color: theme(text-secondary);
}

مساعد استجابي مع كشف نقطة التوقف

// mixin طباعة استجابية
@mixin responsive-font($min-size, $max-size, $min-vw: 320px, $max-vw: 1200px) {
  font-size: $min-size;

  @if $min-size != $max-size {
    @media (min-width: $min-vw) {
      font-size: calc(
        #{$min-size} + #{strip-unit($max-size - $min-size)} *
        ((100vw - #{$min-vw}) / #{strip-unit($max-vw - $min-vw)})
      );
    }

    @media (min-width: $max-vw) {
      font-size: $max-size;
    }
  }
}

@function strip-unit($value) {
  @return $value / ($value * 0 + 1);
}

// الاستخدام
h1 {
  @include responsive-font(24px, 48px);
}

p {
  @include responsive-font(14px, 18px);
}

أنماط واعية بالاتجاه (دعم RTL)

// اتجاه اللغة
$direction: ltr; // يمكن أن يكون 'ltr' أو 'rtl'

@mixin margin-start($value) {
  @if $direction == rtl {
    margin-right: $value;
  } @else {
    margin-left: $value;
  }
}

@mixin margin-end($value) {
  @if $direction == rtl {
    margin-left: $value;
  } @else {
    margin-right: $value;
  }
}

@mixin padding-start($value) {
  @if $direction == rtl {
    padding-right: $value;
  } @else {
    padding-left: $value;
  }
}

@mixin padding-end($value) {
  @if $direction == rtl {
    padding-left: $value;
  } @else {
    padding-right: $value;
  }
}

// الاستخدام
.sidebar {
  @include margin-end(20px);
  // LTR: margin-right: 20px;
  // RTL: margin-left: 20px;
}

.content {
  @include padding-start(30px);
  // LTR: padding-left: 30px;
  // RTL: padding-right: 30px;
}

.icon {
  @if $direction == rtl {
    transform: scaleX(-1); // قلب الأيقونات في RTL
  }
}

تمرين 1: بناء نظام الأزرار

أنشئ نظام أزرار شامل باستخدام شروط @if:

  1. أنشئ @mixin button($size, $variant, $state) يقبل ثلاثة معاملات
  2. خيارات الحجم: small و medium و large (حشو وحجم خط مختلف)
  3. خيارات المتغير: solid و outline و ghost (أنماط خلفية وحدود مختلفة)
  4. خيارات الحالة: normal و disabled و loading (عتامة ومؤشر مختلف)
  5. استخدم عبارات @if متداخلة للتعامل مع جميع التركيبات
  6. أنشئ 6 فئات أزرار مختلفة تجمع بين هذه الخيارات

تمرين 2: دالة التحقق من الموضوع

قم ببناء نظام موضوع مع التحقق:

  1. أنشئ خريطة $themes مع 3 موضوعات على الأقل (light و dark و high-contrast)
  2. يجب أن يحتوي كل موضوع على: خلفية ونص وألوان أساسية وثانوية ونجاح وخطأ
  3. اكتب @function theme-color($theme-name, $color-key) التي تعيد اللون
  4. استخدم @if للتحقق من وجود كل من الموضوع ومفتاح اللون
  5. أرجع رسائل @error مناسبة للمدخلات غير الصالحة
  6. أضف @warn لأسماء الألوان المهملة
  7. أنشئ CSS للبطاقات والأزرار والتنبيهات باستخدام دالة الموضوع الخاصة بك

تمرين 3: نظام تباعد ذكي

أنشئ نظام تباعد ذكي:

  1. اكتب دالة @function smart-space($multiplier, $context)
  2. خيارات السياق: compact و normal و spacious (وحدة أساسية: 4px و 8px و 12px)
  3. استخدم @if للتحقق مما إذا كان $multiplier سالباً أو صفراً أو موجباً
  4. قدم @warn للقيم السالبة
  5. أنشئ @mixin apply-spacing($type, $direction, $multiplier, $context)
  6. النوع: margin أو padding
  7. الاتجاه: all و vertical و horizontal و top و right و bottom و left
  8. أنشئ 10 فئات خدمية باستخدام هذا النظام
ملاحظة: المنطق الشرطي هو واحد من أقوى الميزات في SASS. يتيح لك إنشاء أنظمة أنماط ذكية وتكيفية تستجيب لمتغيرات التكوين. قم دائماً بتضمين التحقق ورسائل الخطأ المفيدة لجعل كودك قابلاً للصيانة.