توجيهات التحكم: الحلقات (@for و @each و @while)
الحلقات في SASS
الحلقات هي واحدة من أقوى الميزات في SASS، مما يمكنك من إنشاء أنماط CSS متكررة بكفاءة. توفر SASS ثلاثة أنواع من الحلقات: @for للتكرار الرقمي، و @each للتكرار عبر القوائم والخرائط، و @while للتكرار الشرطي. تسمح لك توجيهات التحكم هذه بكتابة كود DRY (لا تكرر نفسك) وإنشاء أنظمة فئات خدمية كاملة بعدة أسطر فقط من SASS.
حلقة @for: from...through مقابل from...to
تتكرر حلقة @for عبر نطاق من الأرقام. توفر SASS نوعين: from...through (شاملة القيمة النهائية) و from...to (باستثناء القيمة النهائية).
@for...through (شاملة)
// through تتضمن القيمة النهائية
@for $i from 1 through 5 {
.item-#{$i} {
width: 20px * $i;
}
}
// CSS المجمع:
// .item-1 { width: 20px; }
// .item-2 { width: 40px; }
// .item-3 { width: 60px; }
// .item-4 { width: 80px; }
// .item-5 { width: 100px; }
// مثال عملي: طبقات z-index
@for $layer from 1 through 10 {
.layer-#{$layer} {
z-index: $layer * 10;
}
}
// النتيجة:
// .layer-1 { z-index: 10; }
// .layer-2 { z-index: 20; }
// ...
// .layer-10 { z-index: 100; }
@for...to (باستثناء)
// to تستثني القيمة النهائية
@for $i from 1 to 5 {
.col-#{$i} {
flex: $i;
}
}
// CSS المجمع (لاحظ: يصل فقط إلى 4، وليس 5):
// .col-1 { flex: 1; }
// .col-2 { flex: 2; }
// .col-3 { flex: 3; }
// .col-4 { flex: 4; }
// العد العكسي
@for $i from 5 through 1 {
.priority-#{$i} {
order: $i;
}
}
// النتيجة:
// .priority-5 { order: 5; }
// .priority-4 { order: 4; }
// .priority-3 { order: 3; }
// .priority-2 { order: 2; }
// .priority-1 { order: 1; }
through عندما تريد تضمين الرقم النهائي (الأكثر شيوعاً)، و to عندما تريد التوقف قبله. فكر في through كـ <= و to كـ < في عوامل المقارنة.
إنشاء فئات خدمية باستخدام @for
واحد من أكثر الاستخدامات العملية لحلقات @for هو إنشاء أنظمة فئات خدمية، مشابهة لتلك الموجودة في أطر العمل مثل Tailwind CSS أو Bootstrap.
خدمات الهامش والحشو
// إنشاء خدمات الهامش
@for $i from 0 through 10 {
.m-#{$i} {
margin: #{$i * 4}px;
}
.mt-#{$i} {
margin-top: #{$i * 4}px;
}
.mr-#{$i} {
margin-right: #{$i * 4}px;
}
.mb-#{$i} {
margin-bottom: #{$i * 4}px;
}
.ml-#{$i} {
margin-left: #{$i * 4}px;
}
.mx-#{$i} {
margin-left: #{$i * 4}px;
margin-right: #{$i * 4}px;
}
.my-#{$i} {
margin-top: #{$i * 4}px;
margin-bottom: #{$i * 4}px;
}
}
// النتيجة: .m-0 إلى .m-10، .mt-0 إلى .mt-10، إلخ.
// الاستخدام: <div class="m-4 mt-8">...</div>
// إنشاء خدمات الحشو
@for $i from 0 through 10 {
.p-#{$i} {
padding: #{$i * 4}px;
}
.pt-#{$i} {
padding-top: #{$i * 4}px;
}
.pr-#{$i} {
padding-right: #{$i * 4}px;
}
.pb-#{$i} {
padding-bottom: #{$i * 4}px;
}
.pl-#{$i} {
padding-left: #{$i * 4}px;
}
.px-#{$i} {
padding-left: #{$i * 4}px;
padding-right: #{$i * 4}px;
}
.py-#{$i} {
padding-top: #{$i * 4}px;
padding-bottom: #{$i * 4}px;
}
}
// متقدم: تباعد استجابي
$breakpoints: (
sm: 576px,
md: 768px,
lg: 992px,
xl: 1200px
);
@each $breakpoint-name, $breakpoint-value in $breakpoints {
@media (min-width: $breakpoint-value) {
@for $i from 0 through 10 {
.m-#{$breakpoint-name}-#{$i} {
margin: #{$i * 4}px;
}
}
}
}
// النتيجة: .m-sm-4، .m-md-4، .m-lg-4، إلخ.
خدمات العرض والارتفاع
// إنشاء فئات عرض بالنسبة المئوية
@for $i from 1 through 12 {
.w-#{$i} {
width: percentage($i / 12);
}
.col-#{$i} {
flex: 0 0 percentage($i / 12);
max-width: percentage($i / 12);
}
}
// النتيجة:
// .w-1 { width: 8.33333%; }
// .w-2 { width: 16.66667%; }
// ...
// .w-12 { width: 100%; }
// خدمات العرض الثابت
@for $i from 1 through 20 {
.w-#{$i * 20} {
width: #{$i * 20}px;
}
.h-#{$i * 20} {
height: #{$i * 20}px;
}
}
// النتيجة: .w-20، .w-40، .w-60، ...، .w-400
// النتيجة: .h-20، .h-40، .h-60، ...، .h-400
// خدمات viewport الكاملة
@for $i from 1 through 10 {
.vw-#{$i * 10} {
width: #{$i * 10}vw;
}
.vh-#{$i * 10} {
height: #{$i * 10}vh;
}
}
// النتيجة: .vw-10 إلى .vw-100، .vh-10 إلى .vh-100
حلقة @each: التكرار عبر القوائم
حلقة @each مثالية للتكرار عبر قوائم القيم، مما يجعلها مثالية لإنشاء تنويعات المكونات بناءً على مجموعات محددة مسبقاً من القيم.
@each الأساسي مع القوائم
// تكرار قائمة بسيط
$sizes: small, medium, large, x-large;
@each $size in $sizes {
.button-#{$size} {
@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 if $size == x-large {
padding: 20px 40px;
font-size: 20px;
}
}
}
// خدمات المحاذاة
$alignments: left, center, right, justify;
@each $align in $alignments {
.text-#{$align} {
text-align: $align;
}
}
// النتيجة:
// .text-left { text-align: left; }
// .text-center { text-align: center; }
// .text-right { text-align: right; }
// .text-justify { text-align: justify; }
// خدمات العرض
$displays: block, inline, inline-block, flex, grid, none;
@each $display in $displays {
.d-#{$display} {
display: $display;
}
}
// خدمات الموضع
$positions: static, relative, absolute, fixed, sticky;
@each $position in $positions {
.position-#{$position} {
position: $position;
}
}
نظام الألوان مع @each
// تعريف لوحة الألوان
$colors: (
primary: #007bff,
secondary: #6c757d,
success: #28a745,
danger: #dc3545,
warning: #ffc107,
info: #17a2b8,
light: #f8f9fa,
dark: #343a40
);
// إنشاء خدمات لون النص
@each $name, $color in $colors {
.text-#{$name} {
color: $color;
}
.bg-#{$name} {
background-color: $color;
}
.border-#{$name} {
border-color: $color;
}
.btn-#{$name} {
background-color: $color;
border-color: $color;
color: if(lightness($color) > 50%, #000, #fff);
&:hover {
background-color: darken($color, 7.5%);
border-color: darken($color, 10%);
}
&:active {
background-color: darken($color, 10%);
border-color: darken($color, 12.5%);
}
}
.alert-#{$name} {
background-color: mix(white, $color, 85%);
border: 1px solid mix(white, $color, 70%);
color: darken($color, 10%);
}
}
// النتيجة: نظام ألوان كامل مع فئات .text-* و .bg-* و .border-* و .btn-* و .alert-*
@each مع الخرائط (تفكيك مفتاح-قيمة)
عند استخدام @each مع الخرائط، يمكنك تفكيك كل من المفتاح والقيمة، مما يجعلها مثالية لإنشاء أنماط بناءً على كائنات التكوين.
نظام نقاط التوقف
// خريطة نقاط التوقف الاستجابية
$breakpoints: (
xs: 0,
sm: 576px,
md: 768px,
lg: 992px,
xl: 1200px,
xxl: 1400px
);
// إنشاء عروض الحاوية
@each $name, $width in $breakpoints {
@if $width > 0 {
@media (min-width: $width) {
.container {
max-width: $width - 20px;
}
.container-#{$name} {
max-width: $width - 20px;
}
}
} @else {
.container-#{$name} {
width: 100%;
}
}
}
// خدمات إخفاء/إظهار لكل نقطة توقف
@each $name, $width in $breakpoints {
@if $width > 0 {
@media (min-width: $width) {
.d-#{$name}-none { display: none; }
.d-#{$name}-block { display: block; }
.d-#{$name}-flex { display: flex; }
.d-#{$name}-grid { display: grid; }
}
}
}
// النتيجة: .d-sm-none، .d-md-flex، .d-lg-grid، إلخ.
مقياس الطباعة
// مقياس حجم الخط
$font-sizes: (
xs: 12px,
sm: 14px,
base: 16px,
lg: 18px,
xl: 20px,
2xl: 24px,
3xl: 30px,
4xl: 36px,
5xl: 48px,
6xl: 60px
);
@each $name, $size in $font-sizes {
.text-#{$name} {
font-size: $size;
}
}
// مقياس وزن الخط
$font-weights: (
thin: 100,
light: 300,
normal: 400,
medium: 500,
semibold: 600,
bold: 700,
black: 900
);
@each $name, $weight in $font-weights {
.font-#{$name} {
font-weight: $weight;
}
}
// مقياس ارتفاع السطر
$line-heights: (
none: 1,
tight: 1.25,
snug: 1.375,
normal: 1.5,
relaxed: 1.625,
loose: 2
);
@each $name, $height in $line-heights {
.leading-#{$name} {
line-height: $height;
}
}
@each لإنشاء فئات الأيقونات
خدمات الأيقونات والصور
// أيقونات وسائل التواصل الاجتماعي
$social-icons: (
facebook: #1877f2,
twitter: #1da1f2,
instagram: #e4405f,
linkedin: #0a66c2,
youtube: #ff0000,
github: #333,
discord: #5865f2,
whatsapp: #25d366
);
@each $name, $color in $social-icons {
.icon-#{$name} {
color: $color;
&:hover {
color: darken($color, 10%);
}
}
.btn-#{$name} {
background-color: $color;
color: white;
border: none;
&:hover {
background-color: darken($color, 10%);
}
}
.badge-#{$name} {
background-color: mix(white, $color, 85%);
color: $color;
border: 1px solid mix(white, $color, 70%);
}
}
// خدمات حجم الصورة
$image-sizes: (
xs: 24px,
sm: 32px,
md: 48px,
lg: 64px,
xl: 96px,
2xl: 128px
);
@each $name, $size in $image-sizes {
.avatar-#{$name} {
width: $size;
height: $size;
border-radius: 50%;
object-fit: cover;
}
.icon-#{$name} {
width: $size;
height: $size;
}
}
حلقة @while: التكرار الشرطي
تستمر حلقة @while في التكرار طالما ظل الشرط صحيحاً. إنها أقل استخداماً من @for أو @each، لكنها مفيدة للسيناريوهات الأكثر تعقيداً حيث عدد التكرارات غير معروف مسبقاً.
صيغة @while وأمثلة
// حلقة @while أساسية
$i: 1;
@while $i <= 5 {
.item-#{$i} {
width: 50px * $i;
}
$i: $i + 1; // لا تنسَ الزيادة!
}
// النتيجة:
// .item-1 { width: 50px; }
// .item-2 { width: 100px; }
// .item-3 { width: 150px; }
// .item-4 { width: 200px; }
// .item-5 { width: 250px; }
// قوى العدد 2 (مفيد لمقاييس z-index)
$power: 1;
@while $power <= 1024 {
.z-#{$power} {
z-index: $power;
}
$power: $power * 2;
}
// النتيجة: .z-1، .z-2، .z-4، .z-8، .z-16، .z-32، .z-64، .z-128، .z-256، .z-512، .z-1024
// متتالية فيبوناتشي (للتباعد)
$fib-a: 0;
$fib-b: 1;
$count: 0;
@while $count < 10 {
.fib-space-#{$count} {
margin: #{$fib-b}px;
}
$temp: $fib-a + $fib-b;
$fib-a: $fib-b;
$fib-b: $temp;
$count: $count + 1;
}
// النتيجة: .fib-space-0 (1px)، .fib-space-1 (1px)، .fib-space-2 (2px)،
// .fib-space-3 (3px)، .fib-space-4 (5px)، .fib-space-5 (8px)، إلخ.
دمج الحلقات مع الاستيفاء #{$var}
الاستيفاء ضروري عند استخدام الحلقات لإنشاء أسماء فئات وقيم خصائص ديناميكية. تقوم صيغة #{$variable} بإدراج قيم المتغيرات في المحددات وأسماء الخصائص.
تقنيات استيفاء متقدمة
// الاستيفاء في المحددات
$directions: top, right, bottom, left;
@each $direction in $directions {
.border-#{$direction} {
border-#{$direction}: 1px solid #ccc;
}
.rounded-#{$direction} {
border-#{$direction}-left-radius: 8px;
border-#{$direction}-right-radius: 8px;
}
}
// الاستيفاء مع الحسابات
$steps: 5;
@for $i from 1 through $steps {
$opacity: $i / $steps;
.opacity-#{$i * 20} {
opacity: $opacity;
}
}
// النتيجة: .opacity-20 (0.2)، .opacity-40 (0.4)، ...، .opacity-100 (1)
// استيفاء معقد
$properties: (
m: margin,
p: padding
);
$directions: (
t: top,
r: right,
b: bottom,
l: left,
x: (left, right),
y: (top, bottom)
);
@each $prop-key, $prop-value in $properties {
@each $dir-key, $dir-value in $directions {
@for $i from 0 through 10 {
.#{$prop-key}#{$dir-key}-#{$i} {
@if type-of($dir-value) == list {
@each $side in $dir-value {
#{$prop-value}-#{$side}: #{$i * 4}px;
}
} @else {
#{$prop-value}-#{$dir-value}: #{$i * 4}px;
}
}
}
}
}
// النتيجة: .mt-0، .mt-4، .mt-8، .px-0، .px-4، .my-8، إلخ.
مثال من العالم الواقعي: نظام فئات خدمية كامل
بناء نظام خدمي مشابه لـ Tailwind
// التكوين
$base-spacing: 4px;
$spacing-scale: 0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24, 32, 40, 48, 64;
$colors: (
slate: #64748b,
gray: #6b7280,
red: #ef4444,
orange: #f97316,
yellow: #eab308,
green: #22c55e,
blue: #3b82f6,
indigo: #6366f1,
purple: #a855f7,
pink: #ec4899
);
$breakpoints: (
sm: 640px,
md: 768px,
lg: 1024px,
xl: 1280px,
2xl: 1536px
);
// إنشاء خدمات التباعد
@each $space in $spacing-scale {
$value: $space * $base-spacing;
// الهامش
.m-#{$space} { margin: $value; }
.mx-#{$space} { margin-left: $value; margin-right: $value; }
.my-#{$space} { margin-top: $value; margin-bottom: $value; }
.mt-#{$space} { margin-top: $value; }
.mr-#{$space} { margin-right: $value; }
.mb-#{$space} { margin-bottom: $value; }
.ml-#{$space} { margin-left: $value; }
// الحشو
.p-#{$space} { padding: $value; }
.px-#{$space} { padding-left: $value; padding-right: $value; }
.py-#{$space} { padding-top: $value; padding-bottom: $value; }
.pt-#{$space} { padding-top: $value; }
.pr-#{$space} { padding-right: $value; }
.pb-#{$space} { padding-bottom: $value; }
.pl-#{$space} { padding-left: $value; }
// الفجوة
.gap-#{$space} { gap: $value; }
.gap-x-#{$space} { column-gap: $value; }
.gap-y-#{$space} { row-gap: $value; }
}
// إنشاء خدمات الألوان
@each $name, $color in $colors {
// ألوان النص
.text-#{$name} {
color: $color;
}
// ألوان الخلفية
.bg-#{$name} {
background-color: $color;
}
// ألوان الحدود
.border-#{$name} {
border-color: $color;
}
// إنشاء الظلال (100-900)
@for $shade from 1 through 9 {
$lightness: 95 - ($shade * 10);
.text-#{$name}-#{$shade}00 {
color: scale-color($color, $lightness: $lightness);
}
.bg-#{$name}-#{$shade}00 {
background-color: scale-color($color, $lightness: $lightness);
}
}
}
// خدمات استجابية
@each $breakpoint-name, $breakpoint-value in $breakpoints {
@media (min-width: $breakpoint-value) {
// تباعد استجابي
@each $space in $spacing-scale {
$value: $space * $base-spacing;
.#{$breakpoint-name}\:m-#{$space} {
margin: $value;
}
.#{$breakpoint-name}\:p-#{$space} {
padding: $value;
}
}
// عرض استجابي
.#{$breakpoint-name}\:block { display: block; }
.#{$breakpoint-name}\:flex { display: flex; }
.#{$breakpoint-name}\:grid { display: grid; }
.#{$breakpoint-name}\:hidden { display: none; }
}
}
// النتيجة: مئات من فئات الخدمة مثل:
// .m-4، .px-8، .text-blue، .bg-red-500، .sm:flex، .lg:p-12، إلخ.
تمرين 1: بناء نظام شبكة كامل
أنشئ نظام شبكة من 12 عموداً باستخدام الحلقات:
- استخدم @for لإنشاء فئات .col-1 حتى .col-12 مع عروض مناسبة
- أنشئ فئات الإزاحة .offset-1 حتى .offset-11 باستخدام margin-left
- أنشئ فئات push و pull (.push-1، .pull-1) باستخدام الموضع النسبي
- أضف متغيرات استجابية لنقاط التوقف sm و md و lg و xl (.col-md-6، .col-lg-4، إلخ.)
- قم بتضمين خدمات الفجوة (.gap-1 حتى .gap-8) لتخطيطات grid/flexbox
تمرين 2: نظام الأيقونات والشارات
قم ببناء نظام أيقونات وشارات باستخدام الحلقات:
- أنشئ خريطة لـ 8 منصات وسائط اجتماعية على الأقل مع ألوان علامتها التجارية
- استخدم @each لإنشاء فئات .icon-[name] مع ألوان مناسبة
- أنشئ فئات أزرار .btn-[name] مع ألوان العلامة التجارية
- أنشئ فئات .badge-[name] مع إصدارات خلفية فاتحة
- أضف متغيرات الحجم (sm، md، lg) لكل أيقونة باستخدام حلقات متداخلة
- أنشئ حالات التمرير التي تغمق الألوان بنسبة 10%
تمرين 3: مولد خدمات متقدم
أنشئ مولد فئات خدمية شامل:
- قم ببناء خريطة تكوين مع مقياس التباعد والألوان ونقاط التوقف وأحجام الخطوط
- استخدم حلقات @each متداخلة لإنشاء خدمات margin/padding لجميع الاتجاهات
- أنشئ خدمات لون النص ولون الخلفية ولون الحدود
- أنشئ متغيرات استجابية لجميع الخدمات باستخدام طبقة حلقة أخرى
- أضف متغيرات فئة زائفة (:hover، :focus) لخدمات الألوان
- احسب وسجل العدد الإجمالي للفئات المُنشأة
- إضافي: أضف حلقة @while لإنشاء مقياس تباعد أسي (4px، 8px، 16px، 32px، إلخ.)