CSS اختصار لـ Cascading Style Sheets. كل كلمة فيها معنى مهم:
- Cascading (متدرج) — لما في أكثر من قاعدة بتأثر على نفس العنصر، CSS بيختار الأقوى. هنشرح ده تفصيلاً في درس Specificity.
- Style — بنتكلم عن المظهر: الألوان، الأحجام، المسافات، التخطيط.
- Sheets — بتُكتب في ملفات منفصلة (.css) زي ما HTML في ملفاتها.
كل قاعدة CSS عندها 3 أجزاء: selector (مين اللي هتأثر عليه؟) + property (إيه اللي عايز تغيّره؟) + value (هتغيّره لإيه؟). مثلاً: h1 { color: blue; } — المحدد هو h1، الخاصية color، القيمة blue.
لما المتصفح يفتح صفحتك بيعمل 5 خطوات:
- يقرأ الـ HTML ويحوّله لشجرة عناصر اسمها DOM Tree
- يقرأ الـ CSS ويحوّله لشجرة ثانية اسمها CSSOM Tree
- يدمج الاتنين في "Render Tree" — بس العناصر الظاهرة
- يحسب المواضع (Layout/Reflow) — كل عنصر فين وقد إيه
- يرسم على الشاشة (Paint) — ألوان، ظلال، صور
width وmargin بتعمل إعادة حساب للـ Layout كاملة (Reflow) وده بطيء. لكن properties زي transform وopacity بيشتغلوا على الـ GPU مباشرةً وأسرع بكتير. عشان كده animations بتستخدم transform مش left/top.
أسرع طريقة تتعلم CSS هي DevTools. افتح أي صفحة، اضغط F12، روح Styles panel، وعدّل أي قيمة وهتشوف التغيير فوراً. ده مش غش — ده طريقة كل المحترفين بيجربوا فيها.
في 3 طرق تحط فيها CSS في صفحتك. كل طريقة ليها مكانها الصح، ومش هتستخدم إلا واحدة أساساً.
/* ══ الطريقة 1: External CSS ← دي اللي هتستخدمها دايماً ══ */ /* ملف style.css منفصل، تربطه جوه <head> */ <link rel="stylesheet" href="style.css"> /* ══ الطريقة 2: Internal ← للتجريب السريع فقط ══ */ /* جوه <style> في نفس ملف HTML */ <style> body { background: #000; } </style> /* ══ الطريقة 3: Inline ← تجنبها إلا لو JavaScript محتاجها ══ */ /* مكتوبة مباشرة في الـ HTML element */ <div style="color: red; font-size: 18px">...</div>
ليه External CSS هي الأفضل؟ لأسباب 3:
- الـ Cache — المتصفح بيحفظ الملف في الذاكرة. الزائر الأول يحمّله مرة واحدة، بعد كده كل الصفحات تانية بتاخده من الذاكرة مش من الإنترنت.
- الفصل (Separation of Concerns) — HTML للهيكل، CSS للتصميم. لو غيّرت لون الـ primary color، بتغيّره في ملف واحد ومش بتدور عليه في كل ملفات HTML.
- فريق العمل — مبرمج يشتغل على HTML وdesigner يشتغل على CSS في نفس الوقت بدون تعارض.
أولاً: specificity عالي جداً، بيكسر الـ CSS التانية وصعب تعمل override عليه. ثانياً: مش بيتحفظ في الـ cache. ثالثاً: لو عندك 50 button بنفس الـ style لازم تكرر الكود 50 مرة. الاستثناء الوحيد: لما JavaScript بتغير style ديناميكياً زي element.style.transform = 'rotate(45deg)'.
الـ Selector هو الجزء اللي بتحدد فيه على أي عنصر هتطبق الـ CSS. اختيار الـ selector الصح فرق بين كود نظيف وكود فوضى. في أنواع كتير — هنتعلم الأهم:
/* ── 1. Element Selector: يأثر على كل عناصر من نوعه ── */ h1 { color: #2965f1; } /* كل h1 في الصفحة */ p { line-height: 1.7; } /* كل p في الصفحة */ /* ── 2. Class Selector (.): الأكثر استخداماً ── */ /* يأثر على كل عنصر عنده class="card" في الـ HTML */ .card { background: white; } .card.active { border: 2px solid blue; } /* عنده الاتنين */ /* ── 3. ID Selector (#): لعنصر واحد بس في الصفحة ── */ /* يأثر على العنصر اللي عنده id="hero" — استخدمه نادر في CSS */ #hero { min-height: 100vh; } /* ── 4. Universal (*): كل عنصر في الصفحة ── */ /* بنستخدمه في الـ reset بس */ * { box-sizing: border-box; } /* ── 5. Descendant ( ): عنصر جوّا عنصر تاني ── */ /* كل p جوّا .card — حتى لو متداخل 10 مستويات */ .card p { color: gray; } /* ── 6. Child (>): الأبناء المباشرون فقط ── */ /* li الابن المباشر لـ .nav — مش أي li جوّاه */ .nav > li { display: inline-block; } /* ── 7. Adjacent Sibling (+): العنصر التالي مباشرة ── */ /* أول p جاي بعد h2 مباشرةً */ h2 + p { font-size: 18px; } /* ── 8. General Sibling (~): كل الأخوة بعده ── */ /* كل p جاية بعد h2 في نفس المستوى */ h2 ~ p { color: gray; } /* ── 9. Attribute []: بناءً على خاصية HTML ── */ input[type="email"] { border: 2px solid blue; } a[href^="https"] { color: green; } /* href يبدأ بـ https */ a[href$=".pdf"] { color: red; } /* href ينتهي بـ .pdf */ a[href*="github"] { font-weight: 700; } /* href يحتوي github */ /* ── 10. Multi (,): عدة selectors بنفس الـ style ── */ h1, h2, h3, h4 { font-weight: 700; }
Class vs ID — الفرق الحقيقي: ID لازم يكون فريداً في الصفحة كلها (واحد بس). Class ممكن تتكرر على عناصر كتير. في CSS، استخدم classes دايماً للـ styling لأنها قابلة للاستخدام أكثر من مرة. IDs استخدمها للـ JavaScript التحديد وللـ anchor links (#about).
نصيحة محترف: مش لازم تحفظ كل الـ selectors. الأهم إنك تفهم الـ 4 الأساسية (element، class، id، descendant) وتعرف إن باقيهم موجودين وترجعلهم وقت الحاجة.
كل عنصر HTML — سواء كان h1 أو div أو img — هو في الأساس مستطيل. ومفيش استثناء. الـ Box Model بيقول إن كل مستطيل بيتكون من 4 طبقات متداخلة من الخارج للداخل:
- Margin — المسافة خارج العنصر، بتبعّده عن العناصر التانية
- Border — حدود العنصر، ممكن تكون مرئية أو لأ
- Padding — المسافة داخل العنصر، بين الـ border والمحتوى
- Content — المحتوى نفسه: النص أو الصورة
/* ══ أول حاجة في كل ملف CSS تكتبها — RESET ══ */ *, *::before, *::after { box-sizing: border-box; /* شرحه جاي ↓ */ margin: 0; padding: 0; } .card { /* ── Content ── */ width: 300px; height: 200px; /* ── Padding: مسافة داخلية ── */ padding: 20px; /* كل الجهات الأربعة */ padding: 10px 20px; /* فوق/تحت يمين/شمال */ padding: 5px 10px 15px 20px; /* top right bottom left */ padding-top: 8px; /* جهة واحدة بس */ /* ── Border ── */ border: 2px solid #2965f1; /* سُمك | نوع | لون */ border-radius: 12px; /* زوايا دائرية */ border-radius: 50%; /* دائرة كاملة */ /* ── Margin: مسافة خارجية ── */ margin: 0 auto; /* توسيط أفقي — auto على اليمين واليسار */ margin-bottom: 24px; } /* ══ box-sizing: border-box — ليه مهم؟ ══ */ /* بدونه: width=300px + padding=20px = 340px فعلي! */ /* معاه: width=300px يشمل الـ padding والـ border */ /* يعني الـ 300px هو الحجم النهائي مهما حطيت padding */
في DevTools (F12)، لما تختار أي عنصر هتلاقي في أسفل الـ Styles panel رسم للـ Box Model بالأرقام الفعلية. ده أفضل طريقة تفهم ليه العنصر ده حجمه كده.
1. نسيان نقطة قبل اسم الـ class
card بدل .card، أنت فعلياً بتستهدف عنصر HTML اسمه <card> (مش موجود) بدل العنصر اللي عنده class="card". افحص دايماً في DevTools إن الـ selector بتاعك "مشطوب" (strikethrough) ولا لأ — لو مشطوب يبقى فيه قاعدة أقوى بتلغيه.2. نسيان box-sizing: border-box
box-sizing: border-box، الـ padding والـ border بيتضافوا فوق الـ width المحدد، مش بياخدوا من جواه. الحل: حط *, *::before, *::after { box-sizing: border-box; } في أول كل ملف CSS — قاعدة دايماً بدون استثناء.3. الخلط بين margin و padding
- أنشئ ملف style.css وربطه في <head> بـ <link>
- أضف الـ Reset في أول سطر: *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
- حدد الـ body وغيّر background-color وfont-family وcolor
- استخدم 5 أنواع مختلفة من الـ selectors على عناصر الـ portfolio
- افتح DevTools، اختار أي عنصر، وشوف الـ Box Model بتاعه في الـ Styles panel
- استخدم attribute selector لتنسيق links خارجية (href^="https") بشكل مختلف
- جرّب child selector (>) على قائمة الـ nav وشوف الفرق عن descendant
- "CSS Selectors game CSS Diner" — لعبة ممتعة بتتعلم فيها كل الـ selectors
- "Chrome DevTools CSS overview" — شوف كل حاجة تقدر تعملها في DevTools
CSS بتدعم أكثر من طريقة لكتابة الألوان. كل طريقة ليها مزايا، بس في الآخر بتعطي نفس النتيجة — فرقهم في سهولة التعديل والمنطقية.
.el { /* ── HEX: الأشهر استخداماً، مفيش بصيرة فيه ── */ color: #2965f1; /* #RRGGBB — حمر، أخضر، أزرق بـ hex */ color: #2965f1aa; /* الأخيرين للشفافية (alpha) */ /* ── RGB: أوضح من HEX، الأرقام من 0 لـ 255 ── */ color: rgb(41, 101, 241); color: rgba(41, 101, 241, 0.5); /* 0=شفاف 1=معتم */ /* ── HSL: الأسهل للتفكير والتعديل ── */ /* Hue(الدرجة 0-360) Saturation(التشبع%) Lightness(السطوع%) */ color: hsl(227, 88%, 55%); /* عايز نفس اللون أفتح؟ غيّر 55% لـ 75% */ /* عايز شفافية؟ */ color: hsl(227 88% 55% / 0.5); /* نفس اللون بـ 50% شفافية */ /* ── OKLCH: الأحدث والأدق — 2026 ── */ /* Lightness(0-1) Chroma(تشبع) Hue(0-360) */ color: oklch(60% 0.22 250); /* ميزته: Lightness متسقة — 60% أزرق = 60% أحمر في الإضاءة */ /* ── color-mix: خلط لونين مع بعض ── */ color: color-mix(in srgb, #2965f1 70%, #e91e8c 30%); /* ── Named Colors: 140+ اسم جاهز ── */ color: tomato; color: cornflowerblue; color: transparent; /* شفاف كامل */ /* ══ Backgrounds ══ */ /* لون عادي */ background-color: #f8f9fa; /* linear-gradient: خط من لون للتاني ── */ /* الرقم هو الاتجاه: 0=فوق، 90=يمين، 135=زاوية */ background: linear-gradient(135deg, #2965f1, #e91e8c); /* ممكن أكثر من لونين مع positions */ background: linear-gradient(180deg, #2965f1 0%, /* أزرق من البداية */ #8b5cf6 50%, /* بنفسجي في المنتص */ #e91e8c 100% /* وردي في النهاية */ ); /* radial-gradient: دائري من المركز */ background: radial-gradient(circle at center, #2965f1 0%, transparent 70%); /* صورة خلفية */ background-image: url('PIC/hero-bg.jpg'); background-size: cover; /* يملأ المساحة مع حفظ النسبة */ background-position: center; /* توسيط */ background-repeat: no-repeat; /* صورة + overlay غامق فوقيها — شائع جداً في Hero sections */ background: linear-gradient(rgba(0,0,0,0.6), rgba(0,0,0,0.3)), url('hero.jpg') center/cover no-repeat; /* الـ gradient فوق الصورة، بيخليها أغمق وبيخلي النص مقروء */ }
Typography هي علم وفن إخراج النص. 80% من محتوى الإنترنت نص، يعني لو عارف تتحكم في الخطوط كويس — موقعك بيبقى مختلف تلقائياً. هنتعلم الأساسيات وبعض التأثيرات الاحترافية.
/* ══ استيراد Google Fonts — في أول الـ CSS قبل أي حاجة ══ */ @import url('https://fonts.googleapis.com/css2?family=Cairo:wght@300;400;700;900&family=Space+Mono&display=swap'); /* display=swap: بيعرض fallback font حتى الخط يتحمّل */ body { /* Font Stack: لو الخط الأول مش موجود بيروح للثاني وهكذا */ font-family: 'Cairo', 'Arial', sans-serif; /* Base size: كل rem بيتحسب منها (16px = 1rem) */ font-size: 16px; /* Line-height: المسافة بين الأسطر — بدون وحدة أفضل */ /* 1.7 يعني 1.7 × font-size = 27.2px مسافة بين الأسطر */ line-height: 1.7; } h1 { /* clamp(min, preferred, max) — أقوى طريقة للـ font-size */ /* أصغر من 2rem مستحيل — أكبر من 5rem مستحيل */ /* بينهم بيتمدد بنسبة 5vw من عرض الشاشة */ font-size: clamp(2rem, 5vw, 5rem); font-weight: 900; /* 100=خفيف جداً، 400=عادي، 700=بولد، 900=ثقيل */ letter-spacing: -2px; /* المسافة بين الحروف — سالب = أكثر تقارباً */ line-height: 1.1; /* عناوين بتحتاج line-height أصغر */ } /* ══ Gradient Text — تأثير احترافي جداً ══ */ /* 3 properties لازم يكونوا مع بعض عشان يشتغل */ .gradient-text { background: linear-gradient(90deg, #2965f1, #00d4ff); -webkit-background-clip: text; /* قص الـ background على شكل النص */ background-clip: text; -webkit-text-fill-color: transparent; /* بيخلي لون النص شفاف عشان الـ bg يظهر */ } /* ══ Text Glow — نص بيضيء ══ */ .glow-text { text-shadow: 0 0 20px rgba(41,101,241,0.8), /* ظل قريب وقوي */ 0 0 40px rgba(41,101,241,0.4); /* ظل بعيد وخفيف */ } /* ══ Truncate: نص طويل بيتقطع بـ ... ══ */ /* الثلاثة مع بعض إلزامية عشان يشتغل */ .truncate { white-space: nowrap; /* منع الـ line break */ overflow: hidden; /* إخفاء ما يتجاوز الحدود */ text-overflow: ellipsis; /* إضافة الـ ... */ } /* ══ وحدات الحجم — مهمة تفهمها ══ */ /* px = ثابت دايماً، مش responsive */ /* rem = نسبة من html font-size (عادة 16px) */ /* 1.5rem = 24px — بيتأثر بـ zoom المستخدم */ /* em = نسبة من font-size الـ parent */ /* 1.5em على h2 = 1.5 × حجم font-size الـ parent */ /* vw = نسبة من عرض الشاشة (100vw = عرض الشاشة) */ /* vh = نسبة من ارتفاع الشاشة (100vh = ارتفاع الشاشة) */
بدل @import، استخدم <link> في الـ HTML. الـ link بيحمّل بشكل متوازي، والـ @import بيحمّل الـ CSS أولاً بعدين الخط. الفرق في السرعة ملحوظ على الاتصالات البطيئة. كمان، حمّل الـ weights اللي محتاجها بس — مش كل الـ weights.
تخيل إن عندك 200 عنصر كلهم لونهم #2965f1. القرار فجأة: نغيّر اللون الأساسي لـ أخضر. بدون Variables: بتفتح كل ملف CSS وبتعمل find & replace وبتدعي إنك ماعملتش غلطة. مع Variables: بتغيّر سطر واحد وكل الـ 200 عنصر بيتغيّروا تلقائياً.
/* ══ تعريف المتغيرات في :root ══ */ /* :root هو الـ html element — أعلى مستوى */ /* أي متغير هنا بيكون متاح في كل الصفحة */ :root { /* الألوان — اسمية واضحة */ --primary: #2965f1; --primary-dim: rgba(41,101,241, 0.1); --primary-glow: rgba(41,101,241, 0.4); --accent: #00d4ff; --bg: #020508; --surface: #080c16; --text: #e8eaf6; --text-muted: #7a8baa; --green: #00e676; --pink: #e91e8c; --gold: #f0b429; /* Border radius — اتساق في الزوايا */ --r-sm: 6px; --r-md: 12px; --r-lg: 24px; --r-full: 9999px; /* دائري كامل */ /* المسافات — نظام موحد */ --gap-xs: 4px; --gap-sm: 8px; --gap-md: 16px; --gap-lg: 32px; /* الخطوط */ --font-ar: 'Cairo', sans-serif; --font-mono: 'Space Mono', monospace; /* Transition موحد للأنيميشن */ --transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); --transition-slow: all 0.5s cubic-bezier(0.4, 0, 0.2, 1); } /* ══ استخدامها ══ */ .btn-primary { background: var(--primary); color: white; padding: var(--gap-sm) var(--gap-md); border-radius: var(--r-sm); font-family: var(--font-ar); transition: var(--transition); } .btn-primary:hover { box-shadow: 0 0 20px var(--primary-glow); /* يضيء بلون الـ primary */ transform: translateY(-2px); } /* ══ Dark / Light Mode بـ Variables ══ */ /* الـ default = dark mode */ /* لو المستخدم ضغط على زرار الـ toggle، JavaScript بتحط data-theme="light" على الـ html */ [data-theme="light"] { --bg: #f8f9fa; --surface: #ffffff; --text: #1a1a2e; --text-muted: #6b7280; /* باقي المتغيرات بتفضل زي ما هي */ } /* ══ تغيير Variable بـ JavaScript ══ */ /* document.documentElement.style.setProperty('--primary', '#e91e8c'); */ /* الكود ده بيغيّر --primary للـ pink فوراً بدون reload */ /* ══ Fallback Value: لو المتغير مش موجود ══ */ .el { color: var(--brand-color, #2965f1); /* لو --brand-color مش معرّف، يستخدم الـ fallback */ }
الفرق بين CSS Variables وـ Sass Variables: Sass Variables ($color: blue;) بتتحوّل لـ values ثابتة وقت الـ compile — مش ممكن تغيّرها بـ JavaScript أو بـ media query. CSS Variables (--color: blue;) بتكون حية في المتصفح — JavaScript تقدر تغيّرها، ويورثها العناصر الأبناء، وتتغيّر مع الـ media queries.
1. استخدام Variable قبل ما تتعرّف
2. نسيان وحدة القياس مع الأرقام
3. -webkit-background-clip بدون background-clip العادية
background-clip: text) جنب الـ vendor-prefixed (-webkit-background-clip). بعض المتصفحات لسه محتاجة الـ prefix، لكن المعيار الرسمي هو النسخة بدون -webkit، وتجاهلها قد يكسر الستايل في تحديثات المتصفح المستقبلية.- عرّف CSS Variables كاملة في :root للـ portfolio: ألوان primary وsurface وtext، مسافات، border-radius، fonts
- استورد Google Font مناسب بـ <link> في الـ HTML وطبّقه على الـ body
- طبّق gradient background على الـ hero section وgradient text على اسمك
- استخدم clamp() لـ font-size الـ h1 يكون responsive من غير media queries
- نفّذ Light/Dark Mode: زرار toggle بـ JavaScript يضيف data-theme="light" على الـ html
- استخدم color-mix() لعمل تدرج من الـ primary color
- "coolors.co" — أداة مجانية لتوليد color palettes متناسقة
- "oklch.com" — color picker لـ OKLCH مع تحويل من HEX
- "Google Fonts specimen" — اختبر كيف يبان الخط قبل ما تستخدمه
كل عنصر HTML عنده display افتراضي — h1 هو block، span هو inline. لما بتغيّر الـ display بتغيّر طريقة تفاعل العنصر مع غيره.
| Display Value | ماذا يعني؟ |
|---|---|
block | يأخد سطر كامل، يقبل width وheight |
inline | يأخد حجم محتواه بس، مش بيقبل width/height |
inline-block | inline في التدفق + يقبل dimensions |
flex | التخطيط المرن — الدرس الجاي |
grid | التخطيط الشبكي — بعد Flexbox |
none | بيختفي كامل ومش بياخد مساحة |
| Position Value | ماذا يعني؟ |
|---|---|
static | الافتراضي — بيتبع تدفق الصفحة العادي |
relative | بيتبع التدفق + تقدر تحرّكه بـ top/left |
absolute | يطلع من التدفق — يتموضع نسبياً للـ parent |
fixed | ثابت بالنسبة للشاشة — مش بيتحرك مع الـ scroll |
sticky | relative حتى يوصل لـ threshold معين، بعدين يبقى fixed |
/* ══ Pattern الأشهر: توسيط عنصر بـ absolute ══ */ /* الـ parent لازم يكون relative عشان الـ child يتموضع نسبياً ليه */ .parent { position: relative; } .child-centered { position: absolute; top: 50%; /* 50% من ارتفاع الـ parent */ left: 50%; /* 50% من عرض الـ parent */ transform: translate(-50%, -50%); /* translate(-50%) بينقل العنصر لورا بنص حجمه، فيبقى في المنتص تماماً */ } /* ══ Navbar ثابت فوق الصفحة ══ */ .navbar { position: fixed; top: 0; left: 0; right: 0; /* يمتد على كل العرض */ z-index: 1000; /* يطلع فوق كل العناصر التانية */ /* لازم تضيف padding-top على body بيساوي height الـ navbar */ /* عشان المحتوى ما يتغطاش */ } /* ══ Sticky sidebar — يتبع الـ scroll لحد نقطة معينة ══ */ .sidebar { position: sticky; top: 80px; /* لما يوصل لـ 80px من أعلى الشاشة يبقى fixed */ } /* ══ z-index: مين فوق مين؟ ══ */ /* الأرقام الأعلى = فوق */ .modal { z-index: 2000; } /* فوق الـ navbar */ .tooltip { z-index: 1500; } .navbar { z-index: 1000; } /* z-index بيشتغل بس على positioned elements (مش static) */
الأكثر شيوعاً: نسيت تحط position على العنصر. z-index بيشتغل بس على العناصر اللي عندها position غير static (relative, absolute, fixed, sticky). حط position: relative وهيشتغل.
Flexbox بيشتغل على محورين: المحور الرئيسي (Main Axis) والمحور المتعامد (Cross Axis). الـ flex-direction بتحدد إيه المحور الرئيسي — row (أفقي) أو column (عمودي). وعشان ده، justify-content بيتحكم في المحور الرئيسي، وalign-items في المتعامد.
/* ══ الـ Container (الأب) — هنا بتعرّف الـ flex ══ */ .flex-container { display: flex; /* بيحوّل الأبناء المباشرين لـ flex items */ /* المحور: row=أفقي (افتراضي) | column=عمودي */ flex-direction: row; /* التوزيع على المحور الرئيسي */ justify-content: flex-start; /* البداية */ justify-content: center; /* وسط */ justify-content: flex-end; /* النهاية */ justify-content: space-between; /* مسافات بين العناصر */ justify-content: space-evenly; /* مسافات متساوية */ /* التوزيع على المحور المتعامد */ align-items: stretch; /* (افتراضي) — يتمدد ليملأ */ align-items: center; /* وسط — لتوسيط عمودي! */ align-items: flex-start; /* أعلى */ align-items: flex-end; /* أسفل */ /* الـ wrap: لما الأبناء مش بيدخلوا في صف واحد */ flex-wrap: nowrap; /* (افتراضي) — يضغط الكل في صف واحد */ flex-wrap: wrap; /* ينزل لسطر جديد لو مش في مساحة */ /* المسافة بين الأبناء */ gap: 16px; /* بين كل العناصر */ gap: 16px 8px; /* row-gap | column-gap */ } /* ══ الـ Items (الأبناء) ══ */ .flex-item { /* flex هو shorthand لـ: flex-grow | flex-shrink | flex-basis */ flex: 1; /* يتوسع ليملأ المساحة المتاحة */ flex: 0 0 200px; /* لا يتوسع، لا يتقلص، عرضه 200px */ flex: none; /* = 0 0 auto: مش بيتغير */ /* يتجاوز align-items بتاع الـ container لهذا العنصر بس */ align-self: center; /* ترتيب العرض — بدون تغيير الـ HTML */ /* 0 = الافتراضي، أصغر = يجي أول */ order: -1; /* هيجي قبل باقي العناصر */ } /* ══ Patterns شائعة جداً ══ */ /* Pattern 1: Navbar — logo على اليمين، روابط على اليسار */ .navbar { display: flex; align-items: center; justify-content: space-between; padding: 0 24px; height: 64px; } /* Pattern 2: توسيط تام أفقي وعمودي */ .fully-centered { display: flex; align-items: center; justify-content: center; min-height: 100vh; } /* Pattern 3: Cards Row بتتكسر على الموبايل */ .cards-row { display: flex; flex-wrap: wrap; gap: 20px; } .cards-row .card { flex: 1 1 calc(33% - 14px); /* 3 في صف على ديسكتوب */ min-width: 250px; /* على موبايل: عمود واحد */ }
Flexbox vs Grid — إمتى تستخدم إيه؟
- Flexbox: لما عندك عناصر في صف واحد أو عمود واحد وعايز تتحكم في توزيعهم ومحاذاتهم — navbar، قائمة أفقية، row من cards.
- Grid: لما عندك layout ثنائي الأبعاد — أعمدة وصفوف مع بعض. Page layout، gallery، dashboard. ممكن تستخدم Grid للـ container وFlexbox جوّا كل card.
/* ══ Grid الأساسي ══ */ .grid { display: grid; /* grid-template-columns: بتحدد عدد وعرض الأعمدة */ grid-template-columns: repeat(3, 1fr); /* 3 أعمدة متساوية */ grid-template-columns: 200px 1fr 200px; /* sidebar | main | sidebar */ grid-template-columns: 1fr 2fr; /* العمود الثاني ضعف الأول */ gap: 20px; } /* ══ الـ fr — وحدة CSS Grid الخاصة ══ */ /* fr = fraction = جزء من المساحة المتاحة */ /* 1fr 2fr 1fr = المساحة بتتقسم 4 أجزاء: 1 + 2 + 1 */ /* الأول ياخد ربع، الأوسط نص، الثالث ربع */ /* ══ auto-fill + minmax: Responsive بدون media queries! ══ */ .responsive-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); /* auto-fill: المتصفح بيقرر عدد الأعمدة */ /* minmax(250px, 1fr): كل عمود مش أصغر من 250px، مش أكبر من 1fr */ /* على موبايل: يضغط لعمود واحد لما 250px مش بتدخل */ /* على ديسكتوب: 3 أو 4 أعمدة حسب العرض */ gap: 20px; } /* ══ Template Areas: اسمِ الأجزاء ══ */ /* طريقة بصرية جداً لتعريف الـ layout */ .page-layout { display: grid; grid-template-areas: "header header" /* header يمتد عمودين */ "sidebar main" /* sidebar يسار، main يمين */ "footer footer"; /* footer يمتد عمودين */ grid-template-columns: 270px 1fr; grid-template-rows: auto 1fr auto; min-height: 100vh; } /* كل element بياخد اسمه من المنطقة */ .page-header { grid-area: header; } .page-sidebar { grid-area: sidebar; } .page-main { grid-area: main; } .page-footer { grid-area: footer; } /* ══ Span: عنصر يمتد على أعمدة أو صفوف ══ */ .full-width { grid-column: 1 / -1; } /* من أول لآخر عمود */ .spans-two { grid-column: span 2; } /* يمتد عمودين */ .tall-item { grid-row: span 2; } /* يمتد صفين */ .big-feature { grid-column: 1 / 3; /* من عمود 1 لعمود 3 */ grid-row: 1 / 3; /* من صف 1 لصف 3 */ }
repeat(auto-fill, minmax(250px, 1fr)) — أقوى سطر CSS ممكن تكتبه للـ responsive. المتصفح بيقرر تلقائياً عدد الأعمدة حسب المساحة المتاحة. لو الشاشة ضيقة: عمود واحد. لو متوسطة: اتنين. لو واسعة: ثلاثة أو أكثر. كل ده بسطر واحد وبدون أي media queries.
1. نسيان display: flex على الـ parent
display: flex. من غيرها، المتصفح بيتجاهل الخصائص دي بصمت تماماً وكأنك ماكتبتهاش.2. z-index مش بيشتغل
position غير static (relative، absolute، fixed، sticky). لو العنصر position: static (الافتراضي)، z-index بيتجاهل تماماً. أضف position: relative (أو أي قيمة تانية غير static) وهيشتغل فوراً.3. Grid columns أكتر من المساحة المتاحة
1fr بدل أرقام px ثابتة لما عايز الأعمدة تتمدد مع المساحة المتاحة، أو استخدم minmax() مع auto-fill للـ responsive التلقائي. أرقام px ثابتة بتفضل ثابتة بغض النظر عن حجم الشاشة وده بيسبب overflow أفقي غير مرغوب.- صمّم الـ navbar بـ Flexbox: logo على اليمين، روابط على اليسار، توسيط عمودي
- اجعل الـ navbar fixed أعلى الصفحة مع backdrop-filter: blur(16px)
- صمّم قسم Projects بـ Grid مع auto-fill وminmax
- استخدم grid-template-areas للـ page layout: header + sidebar + main + footer
- اعمل Holy Grail layout كامل بـ Grid Areas
- جرّب Masonry-like layout بـ grid-row: span للـ cards بأحجام مختلفة
- "Flexbox Froggy" — لعبة بتتعلم فيها Flexbox بطريقة ممتعة
- "CSS Grid Garden" — نفس الفكرة لـ Grid
- "backdrop-filter glassmorphism CSS tutorial"
Transition بيقول للـ CSS: "لما أي property تتغير، مش تتغير فجأة — خد وقت ومشي بـ curve معينة". أشيع استخدام: hover effects على الأزرار والـ cards.
ease .3s
bounce
linear
ease .8s
/* ══ الـ transition property ══ */ /* transition: property | duration | timing-function | delay */ .btn { background: var(--primary); color: white; /* all = كل property بتتغير هتتحرك */ transition: all 0.25s ease; } .btn:hover { background: var(--accent); transform: translateY(-3px); box-shadow: 0 8px 24px rgba(41,101,241,0.4); } /* أفضل: حدد الـ properties بدل all — أسرع للـ browser */ .card { transition: transform 0.3s ease, /* كل property على سطر */ box-shadow 0.3s ease, border-color 0.2s ease; } /* ══ Timing Functions — الـ curve اللي بتتحرك عليها ══ */ transition-timing-function: ease; /* سريع في الأول يهدأ في الآخر */ transition-timing-function: linear; /* سرعة ثابتة */ transition-timing-function: ease-in; /* يبدأ بطيء يسرع */ transition-timing-function: ease-out; /* يبدأ سريع يهدأ */ transition-timing-function: ease-in-out;/* يبدأ بطيء، يسرع، يهدأ */ /* cubic-bezier: أي curve تخيلها — موقع cubic-bezier.com يساعدك */ transition-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55); /* bounce! */ /* ══ Accessibility: احترم من عنده مشكلة مع الحركة ══ */ /* بعض المستخدمين بيعانوا من الدوار مع الـ animations */ @media (prefers-reduced-motion: reduce) { *, *::before, *::after { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; } }
Transform بيغيّر المظهر البصري للعنصر بدون ما يأثر على مكانه الفعلي في التخطيط. يعني العناصر التانية مش بتعرف إن العنصر اتحرك — مساحته الأصلية فاضية. ده بيخليه أسرع في الـ rendering لأنه بيشتغل على الـ GPU مباشرةً.
.el { /* ── translate: تحريك العنصر من مكانه ── */ transform: translateX(50px); /* يمين 50px */ transform: translateX(-50px); /* يسار */ transform: translateY(-10px); /* فوق — ده الـ hover lift الشائع */ transform: translate(20px, -10px); /* X وY مع بعض */ transform: translate(-50%, -50%); /* الـ % بتتحسب من حجم العنصر نفسه */ /* ده أشيع pattern للتوسيط مع position absolute */ /* ── scale: تكبير أو تصغير ── */ transform: scale(1.1); /* 10% أكبر */ transform: scale(0.95); /* 5% أصغر — لـ click press effect */ transform: scaleX(1.5); /* يكبر أفقياً بس */ transform: scaleY(0); /* يختفي عمودياً — collapse effect */ /* ── rotate: تدوير ── */ transform: rotate(45deg); /* 45 درجة عكس عقارب الساعة */ transform: rotate(-45deg); /* مع عقارب الساعة */ transform: rotate(0.5turn); /* نص لفة = 180 درجة */ /* ── skew: إمالة ── */ transform: skewX(-10deg); /* ميل أفقي */ transform: skewY(5deg); /* ميل عمودي */ /* ── دمج transforms: بالترتيب من اليمين لليسار ── */ transform: translateY(-5px) scale(1.03) rotate(1deg); /* ── transform-origin: نقطة التحويل ── */ /* الافتراضي center — ممكن تغيّرها لأي زاوية */ transform-origin: top left; /* scale من الزاوية العلوية اليسرى */ transform-origin: center bottom; /* rotate من الأسفل */ } /* ══ مثال عملي: Card Hover Effect ══ */ .project-card { transition: transform 0.3s ease, box-shadow 0.3s ease; } .project-card:hover { transform: translateY(-8px) scale(1.01); box-shadow: 0 20px 40px rgba(0,0,0,0.3); }
Transitions بتعمل حركة بين حالتين (عادي ← hover). لو محتاج حركة أكثر تعقيداً — animation تتكرر وحدها، أو تمر بأكثر من حالتين — هنا Keyframes بييجي.
rotate(360deg)
translateY
translateX
scale
background
box-shadow
/* ══ تعريف الـ Animation ══ */ /* from/to = بداية ونهاية فقط */ @keyframes fadeUp { from { opacity: 0; transform: translateY(30px); } to { opacity: 1; transform: translateY(0); } } /* بالنسب المئوية لـ control كامل على كل مرحلة */ @keyframes bounce { 0% { transform: translateY(0); } 30% { transform: translateY(-20px); } /* طلوع */ 50% { transform: translateY(0); } /* نزول */ 70% { transform: translateY(-10px); } /* ارتداد أصغر */ 100% { transform: translateY(0); } /* نهاية */ } /* ══ تطبيق الـ Animation ══ */ /* animation: name | duration | timing | delay | iteration | direction | fill-mode */ .hero-title { animation: fadeUp 0.6s ease 0.2s 1 normal both; /* both: بيطبق الـ from قبل ما تبدأ والـ to بعد ما تخلص */ } .loader { animation: spin 1s linear infinite; /* تتكرر للأبد */ } /* ══ Staggered: كل element يظهر بتأخير تدريجي ══ */ /* نفس الـ animation لكن delay مختلف لكل عنصر */ .card { animation: fadeUp 0.5s ease both; } .card:nth-child(1) { animation-delay: 0.1s; } .card:nth-child(2) { animation-delay: 0.2s; } .card:nth-child(3) { animation-delay: 0.3s; } /* ══ Skeleton Loading: مؤشر تحميل احترافي ══ */ /* بيبين إن المحتوى بيتحمّل بدل مربعات فاضية */ @keyframes shimmer { 0% { background-position: -200% 0; } 100% { background-position: 200% 0; } } .skeleton { background: linear-gradient(90deg, #1a1a2e 25%, /* لون داكن */ #2a2a4e 50%, /* لون أفتح شوية */ #1a1a2e 75% /* يرجع داكن */ ); background-size: 200% 100%; animation: shimmer 1.5s ease-in-out infinite; border-radius: var(--r-sm); } /* ══ مثال: Floating Image Effect ══ */ @keyframes floating { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-12px); } } .profile-img { animation: floating 3s ease-in-out infinite; }
1. الـ transition على العنصر الغلط
.btn { transition: all .3s; } .btn:hover { transform: scale(1.1); }.2. Animation مش بتشتغل من البداية
animation-fill-mode: both (أو forwards)، العنصر بيرجع لحالته الأصلية (قبل الـ @keyframes) فور انتهاء الأنيميشن، وأحياناً بيبان "flash" غريب قبل ما الأنيميشن تبدأ. أضف both في نهاية الـ animation shorthand دايماً للـ entrance animations.3. استخدام left/top بدل transform للحركة
transform: translateX(20px) بدلاً منها — بيشتغل على الـ GPU مباشرة وأسرع بكثير، خصوصاً مع animations متكررة أو على أجهزة ضعيفة.- أضف transition على كل الأزرار والـ nav links — لما hover اللون يتغيّر بسلاسة
- Card hover effect: translateY(-8px) + box-shadow بـ transition
- @keyframes fadeUp على اسمك والوصف في الـ hero مع animation-delay
- Staggered animation على الـ project cards — كل card تظهر بـ 0.1s تأخير عن السابقة
- أضف prefers-reduced-motion كـ media query لإيقاف كل الـ animations
- Skeleton loading animation على الـ project cards قبل ما تتحمّل
- Floating animation على صورتك الشخصية في الـ hero
- Typing animation على وصفك بـ @keyframes
- "cubic-bezier.com" — بتعمل timing function مخصوصة وبتشوف شكلها
- "CSS animation fill-mode both" — مهم جداً لفهم الـ both
- "Intersection Observer API" — هتحتاجه في JS لتشغيل animations عند الـ scroll
Media Query بيقول: "لو الشاشة كبيرة (أو صغيرة، أو dark mode، أو أي condition)، طبّق الـ CSS ده". الصيغة: @media (condition) { CSS هنا }.
/* ══ Breakpoints المعيارية ══ */ /* Mobile: 0px — 639px (مش محتاج media query - ده الـ base) */ /* Tablet: 640px — 1023px */ /* Desktop: 1024px — ∞ */ /* ── min-width: يطبق CSS من الحجم ده وما فوقه ── */ @media (min-width: 640px) { /* CSS بيتطبق على tablet وdesktop */ .grid { grid-template-columns: repeat(2, 1fr); } } @media (min-width: 1024px) { /* CSS بيتطبق على desktop بس */ .grid { grid-template-columns: repeat(3, 1fr); } .hero h1 { font-size: 5rem; } } /* ── max-width: يطبق CSS لحد الحجم ده ── */ @media (max-width: 767px) { /* CSS بيتطبق على موبايل بس */ .nav-links { display: none; } .hamburger { display: block; } .sidebar { display: none; } .page-layout { grid-template-columns: 1fr; } } /* ── Media Queries لخصائص تانية (مش بس الحجم) ── */ /* المستخدم عنده dark mode في نظامه */ @media (prefers-color-scheme: dark) { :root { --bg: #0d1117; --text: #f0f6ff; } } /* المستخدم قلّل الـ animations في إعداداته */ @media (prefers-reduced-motion: reduce) { * { animation: none !important; transition: none !important; } } /* الجهاز مش بيدعم hover (موبايل/تابلت) */ @media (hover: none) { .tooltip { display: none; } /* tooltips ما تظهرش على touch devices */ } /* الطابعة */ @media print { .navbar, .sidebar, .footer { display: none; } body { font-size: 12pt; color: #000; } } /* ── Range: بين حجمين بالظبط ── */ @media (640px <= width <= 1023px) { /* tablet بس — Modern syntax (2023+) */ }
الـ breakpoints اللي شرحناها (640px, 1024px) هي مجرد معيار شائع — مش قانون. بعض المشاريع بتستخدم 768px لـ tablet. الأهم إنك تختار breakpoints بناءً على محتوى موقعك نفسه، مش بناءً على أحجام أجهزة بعينها لأن الأجهزة بتتغير كل سنة.
في طريقتين للـ responsive design:
- Desktop First (القديمة): تكتب CSS للـ desktop أولاً، وبعدين تضيف media queries بـ
max-widthلتقليلها على الموبايل. - Mobile First (الحديثة): تكتب CSS للموبايل أولاً — بدون media query — وبعدين تضيف media queries بـ
min-widthلتوسيعها على الشاشات الأكبر.
ليه Mobile First أحسن؟ أولاً: المتصفح بيقرأ CSS بالترتيب، الموبايل بياخد أقل كود (أسرع تحميل). ثانياً: أسهل في التفكير — تبني الـ base وتضيف عليه. ثالثاً: Google بيفضّل responsive Mobile First في الـ SEO. رابعاً: لو أضفت feature وما حطتهاش في mobile query، هتكون موجودة على كل الأجهزة تلقائياً.
/* ══ Mobile First: ابدأ بدون media query — ده للموبايل ══ */ /* Navbar: على الموبايل بسيط */ .navbar { display: flex; align-items: center; justify-content: space-between; padding: 0 1rem; height: 58px; } .nav-links { display: none; } /* مخفي على موبايل */ .hamburger { display: flex; } /* ظاهر على موبايل */ /* Hero: عمودي على موبايل */ .hero h1 { font-size: 2.5rem; } .hero { padding: 2rem 1rem; text-align: center; } /* Grid: عمود واحد على موبايل */ .projects-grid { display: grid; grid-template-columns: 1fr; gap: 1rem; padding: 0 1rem; } /* ══ بعدين وسّع للـ Tablet ══ */ @media (min-width: 640px) { .projects-grid { grid-template-columns: repeat(2, 1fr); padding: 0 1.5rem; } .hero { text-align: start; } } /* ══ وبعدين للـ Desktop ══ */ @media (min-width: 1024px) { .navbar { padding: 0 2rem; height: 64px; } .nav-links{ display: flex; } /* يظهر على desktop */ .hamburger{ display: none; } /* يختفي على desktop */ .hero h1 { font-size: 5rem; } .projects-grid { grid-template-columns: repeat(3, 1fr); padding: 0; } .page-layout { grid-template-columns: 270px 1fr; } }
الـ Media Queries بتقفز بين قيم محددة — فجأة الـ font-size تقفز من 2.5rem لـ 5rem. clamp() بيخلي الـ value تتمدد بشكل سلس مع عرض الشاشة بدون قفزات.
/* ══ clamp(minimum, preferred, maximum) ══ */ /* المتصفح بيختار الـ preferred، لكن مش أقل من min ومش أكبر من max */ h1 { /* على موبايل 320px: font-size = max(2rem, min(5vw=16px, 5rem)) = 2rem */ /* على tablet 768px: 5vw = 38.4px ≈ 2.4rem — بيتمدد */ /* على desktop 1200px: 5vw = 60px ≈ 3.75rem — بيتمدد */ /* فوق الحد: 5rem (80px) — بيتوقف */ font-size: clamp(2rem, 5vw, 5rem); } .section-padding { padding: clamp(2rem, 8vw, 6rem); /* بيكبر مع الشاشة */ } .container { max-width: min(1200px, 90vw); /* الأصغر من الاتنين */ /* على شاشة 1400px: 90vw = 1260px — اختار 1200px */ /* على موبايل 400px: 90vw = 360px — اختار 360px */ margin: 0 auto; padding: clamp(1rem, 3vw, 3rem); } /* ══ Viewport Units ══ */ .hero { /* vh = viewport height */ min-height: 100vh; /* 100% من ارتفاع الشاشة */ /* dvh = dynamic viewport height (2023+) */ /* على الموبايل، الـ browser bar بتختفي عند الـ scroll */ /* dvh بيحسب الارتفاع المتاح فعلياً، مش ارتفاع ثابت */ /* بدونه: hero ممكن يتقطع وراء الـ address bar */ min-height: 100dvh; /* ← استخدم ده دايماً للـ hero */ } /* ══ Fluid Space System: نظام مسافات مرن ══ */ /* بدل ما تغيّر كل القيم في media queries، حدد variables مرنة */ :root { --space-xs: clamp(0.5rem, 1vw, 1rem); --space-sm: clamp(1rem, 2vw, 1.5rem); --space-md: clamp(1.5rem, 3vw, 2.5rem); --space-lg: clamp(2rem, 5vw, 4rem); --space-xl: clamp(3rem, 8vw, 6rem); } /* ══ Images دايماً Responsive ══ */ img { max-width: 100%; /* مش تتجاوز حجم الـ container */ height: auto; /* بيحافظ على نسبة الارتفاع */ object-fit: cover; /* لتعبئة مساحة بدون تشويه */ object-position: center; /* التوسيط في المساحة */ }
1. نسيان viewport meta tag في الـ HTML
<meta name="viewport" content="width=device-width, initial-scale=1.0"> في <head>، المتصفح بيحاكي شاشة desktop عريضة على الموبايل، فكل media queries بـ max-width بتتجاهل عملياً.2. الترتيب الغلط لـ Media Queries (Desktop First بالغلط)
3. استخدام vh بدل dvh على الموبايل
min-height: 100dvh (Dynamic Viewport Height) بدلاً منها — بتحسب الارتفاع المتاح فعلياً في كل لحظة.- حوّل الـ CSS للـ Mobile First: اكتب base styles للموبايل، بعدين أضف min-width queries
- الـ navbar: روابطها تختفي على الموبايل — hamburger icon يظهر بدلها
- الـ hero h1: استخدم clamp() لـ font-size مرن بدون media query
- الـ hero: استخدم min-height: 100dvh
- اختبر على DevTools: اضغط Ctrl+Shift+M لـ device simulation — جرّب iPhone وiPad وDesktop
- نفّذ Hamburger menu يفتح ويقفل بـ CSS فقط باستخدام :checked
- أضف @media (prefers-color-scheme: dark) لـ automatic dark mode
- استخدم clamp() لـ section padding كمان
- "CSS clamp calculator utopia.fyi" — أداة بتحسب clamp values مثالية
- "Responsively App" — تطبيق مجاني بيعرض موقعك على كذا جهاز في نفس الوقت
- "CSS-only hamburger menu tutorial"
لما تكتب CSS وتشوفه مش بيتطبق، السبب الأكثر شيوعاً هو Specificity. المتصفح بيحسب "ثقل" كل rule، والأثقل بيفوز بغض النظر عن الترتيب.
الحساب بيتم في 4 أرقام مرتبة: Inline | ID | Class/Pseudo | Element
/* ══ الحساب ══ */ p { color: black; } /* 0-0-0-1: عنصر واحد */ .text { color: blue; } /* 0-0-1-0: class واحد — يفوز على p */ #content .text p { color: red; } /* 0-1-1-1: id+class+element — يفوز */ .nav .link:hover { color: green; } /* 0-0-2-0: class + pseudo-class */ /* ══ الـ Cascade: لما الـ Specificity متساوية ══ */ /* الأخير في الملف يفوز — CSS بيقراه من فوق لتحت */ p { color: red; } p { color: blue; } /* يفوز لأنه جاي بعده */ /* ══ Inheritance: الخصائص اللي بتورثها الأبناء ══ */ /* بترث: color, font-family, font-size, line-height, text-align */ /* مش بترث: margin, padding, border, background, display */ body { color: #333; } /* كل العناصر داخل body هترث color = #333 */ /* إلا لو عندهم rule بيتجاوز ده */ /* ══ Keywords خاصة للـ Inheritance ══ */ .card-link { color: inherit; /* ورّث من الـ parent صراحةً */ color: initial; /* ارجع للـ browser default */ color: unset; /* لو قابلة للإرث: inherit / لو لأ: initial */ all: unset; /* إعادة تعيين كل الـ properties */ } /* ══ !important: استخدامه الصح ══ */ /* المفروض تجنبه — بيشير إن التصميم عنده مشكلة */ /* الاستخدام المقبول الوحيد: override third-party CSS */ .utility-hidden { display: none !important; } /* utility classes محتاجة !important عشان دايماً تتطبق */ /* ══ Debugging: ليه الـ CSS مش بيشتغل؟ ══ */ /* 1. افتح DevTools ← Elements ← Styles */ /* 2. لو الـ property فيها خط: مكسورة أو override */ /* 3. شوف إيه اللي بيفوز وليه — هتشوف الـ specificity */ /* 4. specificity calculator: keegan.st/tool/css-specificity-graph */
مش إضافة !important. الحل: إعادة هيكلة الـ CSS — استخدم classes بدل IDs، وقلّل من التعشيش. لو عندك .nav .link .icon بتحتاج 3 levels لتحديد element، في مشكلة في الـ CSS architecture.
Pseudo-Class بتستهدف عنصر في حالة معينة: :hover، :first-child، :invalid. بتبدأ بـ colon واحدة :
Pseudo-Element بيعمل عنصر وهمي جوّا أو قبل/بعد العنصر الحقيقي. بتبدأ بـ colon double ::
/* ══ Pseudo-Classes المهمة ══ */ /* ── User Actions ── */ a:hover { color: var(--primary); } button:active { transform: scale(0.97); } /* لما يضغط */ input:focus { border-color: var(--primary); } /* :focus-visible أفضل من :focus — بيظهر بس لما تنقّل بالـ keyboard */ a:focus-visible { outline: 2px solid var(--primary); outline-offset: 3px; } /* ── Form Validation ── */ input:valid { border-color: var(--green); } input:invalid { border-color: red; } input:required { border-left: 3px solid var(--gold); } /* :invalid بيطبق من اللحظة الأولى — استخدم :user-invalid لبعد الـ blur */ input:user-invalid { border-color: red; } /* ── Structural: بناءً على مكان العنصر في الـ DOM ── */ li:first-child { border-top: none; } /* أول عنصر */ li:last-child { border-bottom: none; } /* آخر عنصر */ tr:nth-child(even) { background: rgba(0,0,0,.02); } /* صفوف الزوجية */ tr:nth-child(3n+1) { color: var(--primary); } /* كل 3: 1،4،7،10 */ p:not(.special) { color: var(--text-muted); } /* كل p ما عندهاش .special */ /* ══ Pseudo-Elements ══ */ /* ── ::before و::after: عناصر وهمية قبل وبعد المحتوى ── */ /* content إلزامي — حتى لو فارغ */ .required-label::after { content: ' *'; /* نص بعد الـ label */ color: red; } /* تأثير Underline ينمو من اليسار */ .nav-link { position: relative; text-decoration: none; } .nav-link::after { content: ''; /* فارغ — هو بس decoration */ position: absolute; bottom: -2px; left: 0; width: 0; /* يبدأ بعرض صفر */ height: 2px; background: var(--primary); transition: width 0.3s ease; } .nav-link:hover::after { width: 100%; /* يتمدد لكامل العرض */ } /* ── ::placeholder, ::selection, ::marker ── */ input::placeholder { color: var(--text-dim); font-style: italic; } ::selection { background: var(--primary); color: white; } li::marker { color: var(--primary); } /* ── scroll-margin-top: مهم مع الـ sticky navbar ── */ /* لما تضغط anchor link، الصفحة بتسكرول بس الـ section بيتغطى بالـ navbar */ section { scroll-margin-top: 80px; } /* نفس ارتفاع الـ navbar */
CSS Nesting بتخليك تكتب CSS بنفس أسلوب Sass — بدون أي preprocessor. و:has() هي أقوى selector جاء لـ CSS في تاريخها — بتختار عنصر بناءً على محتواه.
/* ══ CSS Nesting (2023+) ══ */ /* بدل ما تكتب .card { } ثم .card h2 { } ثم .card:hover { } */ /* بتكتبهم كلهم جوّا بعض */ .card { background: var(--surface); border-radius: var(--r-md); padding: var(--gap-md); transition: var(--transition); /* & = نفس الـ selector الخارجي (.card) */ & h2 { /* = .card h2 */ font-size: 1.2rem; color: var(--text); margin-bottom: var(--gap-xs); } & p { /* = .card p */ color: var(--text-muted); font-size: 0.9rem; } &:hover { /* = .card:hover */ transform: translateY(-5px); box-shadow: 0 16px 32px rgba(0,0,0,0.3); } /* يمكن تعشيش Media Queries جوّا! */ @media (max-width: 640px) { padding: var(--gap-sm); } } /* ══ :has() — "Parent Selector" ══ */ /* كانت CSS مش بتدعم "اختار الـ parent بناءً على الـ children" */ /* :has() حلّ ده — بتقول "اختار .card لو جوّاه img" */ /* form فيها input invalid — حطّ border حمراء على الـ form كلها */ .contact-form:has(input:invalid) { border-color: red; } /* section فيها صورة — حوّل الـ layout لـ 2 أعمدة */ .about-section:has(img) { display: grid; grid-template-columns: 1fr 1fr; gap: var(--gap-lg); } /* body لما mobile menu مفتوح — منع الـ scroll */ body:has(.mobile-menu.open) { overflow: hidden; } /* nav لما فيها active link — غيّر شكله */ .navbar:has(.nav-link.active) { border-bottom-color: var(--primary); } /* ══ :is() و :where() — selector shortcuts ══ */ /* بدل تكرار نفس الـ style لكل heading */ /* القديمة: */ /* h1 { } h2 { } h3 { } h4 { } */ /* الحديثة: */ :is(h1, h2, h3, h4) { font-weight: 700; line-height: 1.2; color: var(--text); } /* :is() بيرفع الـ specificity لأعلى selector جوّاه */ /* :where() نفس الفكرة بس specificity = 0 — أسهل للـ override */ :where(h1, h2, h3, h4) { font-weight: 700; /* أسهل تعمل override عليه */ }
المشكلة في Media Queries: بتسأل "الشاشة قد إيه؟" مش "المكان اللي العنصر فيه قد إيه؟". يعني لو عندك نفس الـ card في sidebar ضيق وفي main area واسعة، الـ media query مش هتقدر تميّزهم.
Container Queries حلّت ده — بتسأل "الـ container الأب قد إيه؟". النتيجة: components مستقلة تتكيف مع مكانها تلقائياً.
/* ══ الخطوة 1: حوّل العنصر لـ Container ══ */ /* أي element تحط عليه container-type يبقى container */ .card-wrapper { container-type: inline-size; /* يراقب العرض */ /* container-type: size — يراقب العرض والارتفاع */ } /* لو عايز تديه اسم */ .sidebar { container-type: inline-size; container-name: sidebar; } /* ══ الخطوة 2: اكتب Container Query ══ */ /* @container بدل @media */ @container (min-width: 400px) { /* لو الـ container أكبر من 400px */ .project-card { display: flex; /* أفقي */ gap: var(--gap-md); } .project-card img { width: 40%; flex-shrink: 0; } } /* لو الـ container أصغر من 400px: عمودي (الـ default) */ /* ══ باسم محدد ══ */ @container sidebar (min-width: 250px) { /* بيطبق بس على العناصر جوّا .sidebar */ .nav-item { flex-direction: row; } } /* ══ Container Query Units ══ */ .card-title { /* cqw = container query width */ font-size: clamp(1rem, 4cqw, 2rem); /* يتغير بناءً على عرض الـ container — مش الشاشة */ }
قبل كده، كل scroll animation كانت محتاجة JavaScript وIntersection Observer. دلوقتي CSS بتقدر تعملها وحدها. الفكرة: بدل ما الـ animation تتحكم فيها الوقت، بيتحكم فيها مكان الـ scroll.
/* ══ Reading Progress Bar — بدون JavaScript ══ */ @keyframes scaleProgress { from { transform: scaleX(0); } to { transform: scaleX(1); } } .progress-bar { position: fixed; top: 0; left: 0; width: 100%; height: 3px; background: var(--primary); transform-origin: left center; /* animation-timeline: scroll() بيقول "اربط الـ animation بالـ scroll" */ animation: scaleProgress linear; animation-timeline: scroll(); /* بدل ما تعتمد على الوقت، بتعتمد على مكان الـ scroll */ } /* ══ Fade in لما العنصر يدخل الشاشة ══ */ @keyframes appear { from { opacity: 0; transform: translateY(40px); } to { opacity: 1; transform: translateY(0); } } .animate-on-scroll { animation: appear linear both; /* view() بيقول "اربط بـ visibility العنصر في الـ viewport" */ animation-timeline: view(); /* animation-range: من إمتى لإمتى */ /* entry 0%: لما يبدأ يدخل الشاشة */ /* cover 25%: لما 25% منه في الشاشة */ animation-range: entry 0% cover 25%; } /* ══ Fallback للمتصفحات القديمة ══ */ @supports not (animation-timeline: scroll()) { .animate-on-scroll { opacity: 1; /* بيتظهر عادي بدون animation */ transform: none; } } /* ══ Parallax Effect ══ */ @keyframes parallax { from { transform: translateY(0); } to { transform: translateY(200px); } } .hero-bg { animation: parallax linear; animation-timeline: scroll(root block); }
Scroll-driven Animations مدعومة بالكامل في Chrome 115+ وEdge 115+. Firefox 110+. Safari لسه بيضيف الدعم تدريجياً. استخدم @supports كـ fallback، أو JavaScript مع Intersection Observer للـ full support. في كورس JavaScript هنتعلم Intersection Observer.
1. !important مالهاش حل غير !important تاني
2. Nesting بدون & في الاستخدام الصح
& h2 { }. ده بيخلي قصدك واضح للقارئ التاني (وللمتصفحات الأقدم اللي ممكن تتعامل مع الكتابتين بشكل مختلف).3. :has() مش مدعومة في كل المتصفحات بعد
- حوّل الـ CSS المتكرر لـ Nesting: كل .card + .card h2 + .card:hover يبقوا جوّا .card { }
- أضف ::selection بلون الـ primary color
- أضف scroll-margin-top على كل section
- استخدم :has() في حالة واحدة — مثلاً body:has(.menu.open) { overflow: hidden }
- استخدم ::before أو ::after لإضافة decorative element بدون HTML
- أضف scroll progress bar بـ CSS فقط: animation-timeline: scroll()
- استخدم Container Queries على الـ project cards
- نظّم CSS بـ @layer: base, components, utilities
- "CSS specificity calculator" — بيحسب أي selector specificity
- "scroll-driven animations Chrome demo" — أمثلة رائعة من Google
- "CSS @layer explained" — كيف تنظم CSS بالطبقات
/* === RESET === */ ← /* === VARIABLES === */ ← /* === BASE === */ ← /* === COMPONENTS === */ ← /* === RESPONSIVE === */. الترتيب مهم.position:fixed + backdrop-filter:blur(16px) + border-bottom شفاف. Flexbox للتوزيع. Transition على الـ background عند الـ scroll (JavaScript لاحقاً). على موبايل: roابط تختفي وhamburger يظهر.min-height:100dvh + gradient background + gradient text على الاسم + clamp() لـ h1 + avatar مع border وglow + fadeUp animation عند التحميل + scroll indicator متحرك.object-fit:cover + overlay عند hover + staggered fadeUp animation + technology badges.:focus animation على الـ border. :valid/:user-invalid للـ visual feedback. Submit button مع hover animation.- style.css منظم بـ sections وcomments واضحة
- CSS Variables كاملة في :root — أي تغيير في اللون من مكان واحد
- Navbar: fixed + glass effect + responsive
- Hero: 100dvh + gradient + animation + responsive
- Projects: Grid auto-fill + hover effects
- Mobile First CSS — اختبر على DevTools mobile
- رفع على GitHub Pages — شارك الرابط
- Light/Dark Mode بـ data-theme + CSS Variables + toggle button
- Lighthouse في Chrome DevTools — اوصل لـ 90+ في Performance وAccessibility
- جرّب OKLCH للألوان وشوف الفرق
- "CSS minifier online" — ضغط الـ CSS للـ production
- "Sass SCSS beginner tutorial" — الخطوة الجاية
- "Tailwind CSS crash course" — مكتبة CSS utility-first شائعة جداً