المحتوى عن 'متجاوب'.



مزيد من الخيارات

  • ابحث بالكلمات المفتاحية

    أضف وسومًا وافصل بينها بفواصل ","
  • ابحث باسم الكاتب

نوع المُحتوى


التصنيفات

  • التخطيط وسير العمل
  • التمويل
  • فريق العمل
  • دراسة حالات
  • نصائح وإرشادات
  • التعامل مع العملاء
  • التعهيد الخارجي
  • التجارة الإلكترونية
  • مقالات ريادة أعمال عامة

التصنيفات

  • PHP
    • Laravel
    • ووردبريس
  • جافاسكريبت
    • Node.js
    • jQuery
    • AngularJS
    • Cordova
  • HTML5
  • CSS
    • Sass
    • إطار عمل Bootstrap
  • SQL
  • سي شارب #C
    • منصة Xamarin
  • بايثون
    • Flask
    • Django
  • لغة روبي
    • إطار العمل Ruby on Rails
  • لغة Go
  • لغة جافا
  • لغة Kotlin
  • برمجة أندرويد
  • لغة Swift
  • لغة R
  • لغة TypeScript
  • سير العمل
    • Git
  • صناعة الألعاب
    • Unity3D
  • مقالات برمجة عامة

التصنيفات

  • تجربة المستخدم
  • الرسوميات
    • إنكسكيب
    • أدوبي إليستريتور
    • كوريل درو
  • التصميم الجرافيكي
    • أدوبي فوتوشوب
    • أدوبي إن ديزاين
    • جيمب
  • التصميم ثلاثي الأبعاد
    • 3Ds Max
    • Blender
  • مقالات تصميم عامة

التصنيفات

  • خواديم
    • الويب HTTP
    • قواعد البيانات
    • البريد الإلكتروني
    • DNS
    • Samba
  • الحوسبة السّحابية
    • Docker
  • إدارة الإعدادات والنّشر
    • Chef
    • Puppet
    • Ansible
  • لينكس
  • FreeBSD
  • حماية
    • الجدران النارية
    • VPN
    • SSH
  • مقالات DevOps عامة

التصنيفات

  • التسويق بالأداء
    • أدوات تحليل الزوار
  • تهيئة محركات البحث SEO
  • الشبكات الاجتماعية
  • التسويق بالبريد الالكتروني
  • التسويق الضمني
  • استسراع النمو
  • المبيعات

التصنيفات

  • إدارة مالية
  • الإنتاجية
  • تجارب
  • مشاريع جانبية
  • التعامل مع العملاء
  • الحفاظ على الصحة
  • التسويق الذاتي
  • مقالات عمل حر عامة

التصنيفات

  • الإنتاجية وسير العمل
    • مايكروسوفت أوفيس
    • ليبر أوفيس
    • جوجل درايف
    • شيربوينت
    • Evernote
    • Trello
  • تطبيقات الويب
    • ووردبريس
    • ماجنتو
  • أندرويد
  • iOS
  • macOS
  • ويندوز

التصنيفات

  • شهادات سيسكو
    • CCNA
  • شهادات مايكروسوفت
  • شهادات Amazon Web Services
  • شهادات ريدهات
    • RHCSA
  • شهادات CompTIA
  • مقالات عامة

أسئلة وأجوبة

  • الأقسام
    • أسئلة ريادة الأعمال
    • أسئلة العمل الحر
    • أسئلة التسويق والمبيعات
    • أسئلة البرمجة
    • أسئلة التصميم
    • أسئلة DevOps
    • أسئلة البرامج والتطبيقات
    • أسئلة الشهادات المتخصصة

التصنيفات

  • ريادة الأعمال
  • العمل الحر
  • التسويق والمبيعات
  • البرمجة
  • التصميم
  • DevOps

تمّ العثور على 5 نتائج

  1. في الوقتِ الذي تكبر فيه إدارة الأعمال على الإنترنت بشكلٍ كبير، يتزايد عددُ العملاء الرّاغبين في دفع مبالغ كبيرة من المالِ للأشخاص الذين يمتلكون مهارةَ بناءِ المواقع. ربّما تكون قد تعلَّمت طريقة بناء موقعٍ بواسطة HTML و CSS، و لكن للأسف ذلك النّوع من المواقعِ لا يلّبي احتياجاتِ كُبرياتِ الشَّركات، بل لا تُلبِّي حتّى احتياجات مدوّنة في أيامنا هذه. هذا هو أهمُّ سببٍ يجعل من الضروريِّ لك أن تتعلَّم طريقة تحويل قالب html و css ساكِن إلى قالب ووردبريس متفاعِل. الموارد التِّي ستحتاجها لإكمال هذا الدّرسِ: قالب NeoBlog مصنوع من html و css. معرفة أساسيّة بأكواد ووردبريس. معرِفةٌ متوسِّطة بلغةِ PHP ومكتبة jQuery. ما سنقوم بإنجازه: قالب ووردبريس هو وسيلتُك لتغيير الشَّكل الخارجيِّ لموقعك. القالب يُمكن أن يقوم بتغيير كبيرٍ للشَّكلِ العامِّ لموقعك، حيثُ أنّه يوفِّر لك واجهة رسومِيَّةً عن طريق ملفّاتهِ. عِند إنشاء قالب ووردبريس عليك الانتباه للنقاطِ التاليّة: انتبه لاسمِ ملفّات القالب التي أنشأتها، في الوضع الافتراضي ووردبريس يتعّرف على أسماء ملّفات القالب الافتراضيّة مثلَ single.php و page.php لذا أنصحك بأن تتحقّق من اسم القالب الافتراضيِّ لووردبريس قبل أن تبدأ بتسميةِ ملفاتك الخاصّة. اطّلع على دليل ووردبريس من أجل بعض الدوالِّ، الوسوم أو بعضِ أكواد PHP لاستخدامها في ملفَّات قالبك، هذا الأمر سيبقيك على الطّريق الصحيح للوصولِ إلى الوظائف التي تريد من قالبك تنفيذها. في بعضِ الأحيان قد تحتاج بعضَ ملفّاتِ الـ jQuery لتحسينِ الشَّكل الخارجيِّ لقالبك أو لإضافة بعضِ الوظائف. ووردبريس لن يقوم بتوفير كلِّ شيءٍ لك. تأكّد من كتابة برمجيّةٍ بِهيكلة جيِّدة، خاليّةٍ من أخطاء PHP و HTML مصادقٍ عليه. استخدم CSS نقيّا مصادقًا عليه. اتبع دليل التصميم لتصميم الموقع ونُسوقِه. اعمل نُسخًا احتياطيّةً لملفّاتك. أنت لن تخسر شيئًا عند القِّيام بذلك. لذا وجب عليك دائمًا أخذ معايير وقائيّة عن طريق عمل نُسخٍ احتياطيّة لملفّات قالبك، الإضافات التي تستخدمها إضافة الى بعض الملفّات الأخرى التي تستخدمها في تطوير قالبك. لِمَ يتوجّبُ عليك أخذ ملاحظات حولَ PHP: PHP هي لغة برمجة لإنشاء مواقع ديناميكيّة متفاعلة. هي مستخدمة على نطاقٍ واسعٍ بسبب كونها لغة مفتوحةَ المصدر وهي مناسبة لك كونك تستطيع تضمينها داخل وُسوم HTML. تّمت كتابة ووردبريس باستخدام لغة PHP ومثلَها يُعتبر سكريبت ووردبريس مفتوحَ المصدر مصادقٌ عليه من طرفِ مبادرةِ المصدرِ المفتوح. بعض أجزاء برمجيّة ووردبريس المكتوبة بلغة PHP متعارضة في نسقها لذا وجب أن تكونَ لديك معرفة بأساسيّات لغة PHP. على كلِّ حالٍ لست مضطرًّا لأن تكون مطوِّر مواقع PHP لكي تتمكّن من إنشاءِ قالب ووردبريس، كلُّ ما ستحتاجه هو امتلاك قاعدة قويِّةٍ في لغات HTML، CSS، و JavaScript مع خلفيّة عن أساسيّات PHP. مخطّط العمل: أوّلا، سنقوم بالتعرّف على الملفّات التي سنحتاجها لبناء قالب ووردبريس مُتكامل. الخطوة التاليّة ستكون البدء بكتابة الأكواد داخل كلِّ ملفٍّ من ملفّات القالب. طيلة الطريق، ستكون هناك دوالّ قد لا تبدو واضحةً بالنّسبة لك. في هذه الحالةِ دليل ووردبريس سيُصبح أحسن صديقٍ لك. بعد الانتهاء من إنشاء القالب سنقوم بإضافة المحتوى إليهِ للتأكّد مِن أنَّ البرمجيّة تعمل بالشكل المطلوب على موقعك. فلنبدأ للبدءِ بتحويل قالب NeoBlog لقالبِ ووردبريس فلنقم أوّلًا بإنشاء مجلّدٍ في المسار content/themes. ستجده بداخل المجلّد الذي نصَّبت به ووردبريس. خذ في الحُسبان أنَّ اسم المجلَّد يجب أن يكون نفس اسم القالب الذي تريد إنشاءه. في هذا الدّرس قمت بتسمية قالبي NeoBlog. الجزء الأوّل: 1. ملفّات القالب بالمقارنة مع قالب HTML و CSS ساكن، قالبُ ووردبريس يحتوي على العديد من الملفّات. هذه الملفّات هي مَن تحتوي على الأكوادِ التِّي تجعلُ قالب ووردبريس يعمل. للقيامِ بذلك قم بإنشاء الملفّات التاليّة داخل مجلّدِ قالب NeoBlog: مجلّد CSS: هذا المجلّد سيحتوي على جميع ملفّات التنسيقات، قم بنسخهِ من مجلّد قالب HTML وCSS NeoBlog. مجلّد الخطوط: هذا المجلّد سيحتوي على جميع الخطوط الخارجيّة المستخدمة في التصميم. قم بنسخه أيضًا من مجلّد قالب HTML وCSS NeoBlog. مجلّد الصُّور: هذا المجلّد سيحتوي على جميع الصّور المستخدمةِ في التّصميم مثل الشعار وغيره. قم بنسخه أيضًا من مجلّد قالب HTML وCSS NeoBlog. مجلّد جافا سكريبت: يحتوي كلّ ملفّات جافا سكريبت المستخدمة في قالبنا. هذا المجلّد أيضًا قم بنسخه من مجلّد قالب HTML وCSS NeoBlog. header.php : هذا الملّف سيحتوي على أكواد القِسم الرأسيِّ للقالب. footer.php : هذا الملّف سيحتوي على أكواد القِسم السفليِّ للقالب. index.php : هذا هو الملفّ الأساسي للقالب، سيحتوي على أكواد المنطقة الرئيسيّة للقالب كما سيحدّد مِن أين سيتّم تضمين الملفّات الأخرى. functions.php : هذا الملّف سيتصرّف كإضافة ووردبريس، بحيث أنّه يضيف مميّزاتٍ و خصائص للقالب. single.php : سيحتوي على نسق صفحة عرض المقال. page.php : سيحتوي على نسق صفحة ووردبريس. page-about.php : هذا الملفُّ عبارة عن نموذج مقالٍ خاصٍّ يأتي مدمجًا داخل ووردبريس، يُستخدم هذا النموذج عادة لجعل صفحةٍ أو مجموعة صفحاتٍ تُعرض بشكلٍ مختلفٍ عن الشّكل العام للصّفحة في القالب. في حالتنا سنقوم بإنشاء نموذجٍ خاصٍّ لصفحة “عن الموقع”. page-contact.php : نوع آخر من النّماذج المدمجة مع ووردبريس. هذا النّموذج سيتم استخدامه لِعرضِ استمارة “تواصل معنا” للصّفحة التي تم تحديدها. content-search.php : هذا الملفّ يحتوي على حلقة تِكرارٍ تقوم بعرض نتائج البحث. search.php : هذا الملفّ مسؤولٌ عن عرضِ صفحات نتائج البحث. searchform.php : هذا الملفّ سيحتوي على نسقِ استمارة البحث. comments.php : هذا الملفّ سيحتوي على الكود المسؤول عن عرضِ التعليقات. sidebar.php : هذا الملفّ سيحتوي القائمة الجانبيّة. 404.php : هذا الملفّ سيحتوي على صفحة الخطأ التي ستعرض نصَّ “لم يتم إيجاد نتائج مطابقة” و سيتّم التوجيه إلى هذه الصّفحة عندما لا تكون نتائج الاستعلام المطلوب موجودة بقاعدة البيانات. style.css : هذا الملفّ سيحتوي على تنسيقات و معلومات قالبنا NeoBlog. Screenshot : صورة بصيغة png تقوم بعرضِ تصميم القالب أو عنوانه. طيلة هذا الدّرسِ سنقوم بإضافة أكوادٍ لهذه الملفّات تتضّمن قوائم، مُعلِّقات، حلقات تِكرار وَوِدجت. الخطوة 1.1- نسخ الملفّات الضرورية لقالب ووردبريس NeoBlog بدايةٌ قم بنسخِ مجلّداتcss, fonts, images و js من مجلّد قالب HTML وCSS NeoBlog و ألصقها في قالب ووردبريس NeoBlog. الخطوة 2.1- تسميّة قالبك ووردبريس عن طريق style.css قبل البدءِ بالعمل على ملفّات القالب، فلنقم أوّلًا بشبك ملفّات تنسيق قالب HTML وCSS NeoBlog داخل ملفِّ قالبنا NeoBlog لووردبريس. بداية أنشئ ملفَّ style.css ثمّ ألصق الكود التّالي بداخله. /* Theme Name: NeoBlog Theme URI: https://1stwebdesigner.com/ Author: Sam Norton Author URI: https://1stwebdesigner.com/ Description: A Simple Blog Theme for 1stwebdesigner.com Version: 1.0 License: GNU General Public License v2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html */ @import url("css/reset.css"); @import url("css/bootstrap.css"); @import url("css/style.css"); لا شيء معقّدٌ هنا، هذا الكود يحتوي معلوماتٍ عن القالب مثل اسم القالب، المؤلِّف، رابط موقع المؤلِّف وغيرها. جميع المعلومات مُضمّنة داخل وسم تعليق. هذه المعلومات ستُسهِّل التعرّف على القالب أثناء التنصيب. يحتوي هذا الكود أيضا على مجموعةٍ وُسوم @import وظيفتها استدعاء ملفّات CSS الخاصّة بقالبنا من مجلّد CSS الذي سبق وأن أنشأناه من قبل. الخطوة 3.1- إنشاء صورة مصغّرة للتعريف بالقالب الآن وبعد أن أعطينا اسمًا لقالبنا عبر ملفِّ style.css، فلنقم بإضافة صورة تعريفيّة للقالب. بداية قم بإنشاء صورةٍ بأبعاد 880*660 بِكسل عن طريق فوتوشوب ثم بعد ذلك أضف لها عنوان القالب، تعريفٌ مختصر واسم مؤلِّف القالب. في الحقيقة هذه الصورة ستُعرض بأبعاد 387*290 بِكسل في لوحة تحكّم المدير ولكّننا تعمّدنا إنشاءها بحجم مضاعف حتى تظهر بشكل أوضح على الشاشات الفائقة الدّقة HiDPI. بعد ذلك قم بحفظِ الصورة بامتداد png داخل المجلّد الرئيسي لقالب NeoBlog لووردبريس. الخطوة 4.1- تفعيل القالب بعد إضافتنا للصورة فلنقم الآن بتفعيل القالب. لكن قبل تفعينا للقالب، ولِكي نقوم بالتحقّق إن كان يعمل أم لا، فلنقم بإنشاء ملّف index.php فارغ داخل المجلّد الرئيسي لقالب NeoBlog لووردبريس (لا تقلق، سنضيف الأكواد المناسبة لهذه الصّفحة لاحقًا). الآن دعنا نُفعِّل القالب من خلال لوحة تحكُّمِ المدير عن طريق اختيار مظهر< قوالب ثمّ بعد ذلك نضغط على زرِّ تفعيل. إن أردت التحقّق أنَّ القالب يعمل، كلُّ ما عليك القّيام به هو التوجّه إلى الصفحة الرئيسّية وستلاحظ ظهور صفحةٍ بيضاء فارغة وهذا راجع إلى أنّنا لم نقم بعد بإضافة أكواد لصفحة index.php. أهمُّ المشاكلِ التي قد تواجهك خلال هذا الدّرس خلال تجريبك لهذا الدّرس ، قد تواجهك مشاكل أثناء دمج أكواد PHP مع أكواد HTML. يجب عليك دائمًا التحقّق مرّتين من أكوادك، أحيانًا قد تنسى غلق حلقة التّكرار “while” أو غلق الشّرط “if”. مثلًا في الكود أسفله هناك خطأ حيث أنّنا نسينا إغلاق شرط “if”. <?php if ( have_posts() ) { while ( have_posts() ) { the_post(); // // Post Content here // } end while; } // end if supposed to be here ?> لكنّنا قمنا بإصلاح ذلك هنا: <?php if ( have_posts() ) { while ( have_posts() ) { the_post(); // // Post Content here // } end while; } // end if supposed to be here ?> عليك أيضًا الانتباه عند نهاية كوده PHP لغلقِه بواسطة وسمِ الإغلاق، إن نسيت ذلك فإنّ الكود الخاصّ بك قد لا يعمل عندما يتداخل مع أكواد HTML. إن أردت أن تتعلّم إضافة وظائف أخرى لموقعك فعليك بالاطلاع على دليل ووردبريس . ماهي حُدود هذه السلسلة ؟ رغم أنَّ هذا الدّرس يعطيك مدخلًا لكيفيّة إنشاء قالبك الخاص، إلَّا أنّ لديه حدودًا، فهو سيعلّمك فقط استخدام المميّزات الأساسيّة لإنشاء قالب مدوّنةٍ مثل القائمة الجانبيّة، التذييل، صندوق البحث، قائمة المقالات، الصورة البارزة وغيرها. ملخص الجزء الأوّل جميل! لقد أكملنا الجزء الأوّل من هذا الدّرس. لقد تعلّمت طريقة تضبيط ملفّات القالب، ما يجب عليك تفاديه، بعض النقاط التي وجب عليك البحث عنها، إضافةً إلى الخطوات الواجب إتّباعها لإنشاء قالب ووردبريس. ملفّ HTML مجهّز الآن للبدء بتحويله إلى مجموعة ملفّات ستُشكّل قالب ووردبريس. في الدرس القادم سنعمل على هذه الملفّات ونضيف لها بعض الدّوال لجعل قالبنا يدعم بعض الوظائف. ترجمة -وبتصرّف- للمقال How to Convert a Static HTML Template into a Responsive WordPress Theme
  2. من المؤكد أنك قمت بتصفح الإنترنت من هاتف محمول، ستلاحظ في كثير من المواقع زر فتح قائمة التنقل الموجود في أعلى الموقع الذي بمجرد أن تضغط عليه يقوم بفتح قائمة تنقل الموقع. إضافة هذا الزر إلى موقع ووردبريس الخاص بك سوف تحسن تجربة المستخدم بشكل كبير للمستخدمين الذين يزورون الموقع من أجهزة الهاتف المحمول أو الأجهزة اللوحية. صحيح بأنه يمكنك استعمال إضافة ووردبريس جاهزة للقيام بهذه المهمة أو تنصيب قالب يحوي هذه الميزة ولكن ماذا لو كنت تستعمل قالب خاص بك وتريد إضافة هذه الميزة بنفسك؟ في هذه المقالة سوف أريك كيف تقوم بعمل هذا بمساعدة ووردبريس وبعض تعليمات css وjavascript وتحويل القائمة إلى الشكل المطلوب. ماذا ستحتاج لتتابع مع هذا المقال، ستحتاج إلى: موقع ووردبريس على حاسوبك الذي يحتوي قائمة. قالب ابن للقالب الذي تعمل عليه من أجل أن تبقى تعديلاتك في مكانها عندما يتم تحديث هذا القالب من الإنترنت. سأقوم بتنفيذ هذه التعليمات على موقعي الخاص، الذي يستهدف قائمة التنقل الرئيسية والتي في موقعي الخاص تملك اسم صنف css وهو menu.main ، إذا كان اسم هذا الصنف مختلفًا في موقعك فسوف تقوم بتطبيق التعليمات على اسم الصنف الخاص بقائمتك. القائمة الحالية قائمتي تبدو على أجهزة الحاسب بهذا الشكل: لكن على شاشة الهاتف ليست جميلة: يمكنني تحسين ذلك بجعل محاذاة القائمة إلى المنتصف لكن ذلك سيأخذ الكثير من مساحة الشاشة لذلك سنقوم بعمل زر القائمة الذي بمجرد الضغط عليه سوف تظهر القائمة التي سنقوم بإخفائها في أجهزة الهاتف المحمول. إضافة زر القائمة الخطوة الأولى هي إضافة أيقونة زر القائمة. سوف نقوم بذلك في ملف header.php في قالبك أضف رابط تحت قائمة التنقل الرئيسية ها هو الخاص بي: <?php wp_nav_menu( array( 'container_class' => 'main-nav', 'theme_location' => 'primary' ) ); ?> <a class="toggle-nav" href="#">☰</a> هذا الرابط سيقوم بعمل زر الذي يقوم بفتح وإغلاق زر القائمة وله اسم الصنف togglenav وبداخله الأيقونة وهي في هذه الحالة رمز html. هذا كل ما يتطلب إضافته إلى ملف header.php إذا قمت بإعادة تحميل صفحتك سترى بأن زر القائمة قد ظهر على الشاشة: إخفاء زر القائمة على الشاشات الكبيرة لنبدأ مع شاشات الحاسب ونضيف هذه التعليمات، لكن لا تنسى وضع هذه التعليمات ضمن media query المناسبة لعرض الشاشة: .toggle-nav { display: none !important; } هذا يجعل الرابط الذي يحوي الأيقونة بداخله يختفي. لقد أضفت كلمة important! حتى تبقى هذه التعليمة هي الأقوى و لا يتم الكتابة عليها من قبل التعليمات الآخرى. لقد اختفى. سوف نقوم بإخفائه أيضًا في الشاشات الأصغر من ذلك و لكن سنأتي لهذا لاحقًا. إضافة تنسيقات css لزر القائمة الأن نحن نحتاج لإضافة بعض التنسيقات إلى القائمة في شاشات الهاتف والتي تظهر عندما يضغط المستخدم زر القائمة. أولًا، في ملف css الخاص بك أضف media query المناسبة: @media screen and ( max-width: 480px) { } لقد استهدفت الشاشات التي يبلغ أكبر عرض لها 480px ولكن يمكنك اختيار الأبعاد المناسبة لقائمتك. الآن لنقم بإضافة بعض تعليمات css في media query أولًا سنقوم بإظهار الأيقونة مجددًا و نقوم بوضع تنسيقات لها لتبدو أفضل: .toggle-nav { padding: 15px; margin: 15px; display: inline-block !important; color: #8D7F68; color: #fff; transition: color linear 0.15s; } .toggle-nav:hover, .toggle-nav.active { text-decoration: none; color: #8D7F68; } الأن لنقم بتنسيق القائمة نفسها و إضافة هذه التعليمات في نفس media query : .menu.main { display: inline-block; position: relative; background: #fff; } .menu.main ul { display: none; position: absolute; top: 80%; left: 0px; padding-left: 15px; background: #fff; } .menu.main li { display: block; float: none; } والآن الخطوة الأخيرة هي إضافة javascript المسؤولة عن إظهار القائمة عندما يضغط المستخدم على زر الأيقونة. إضافة سكربت javascript هذه الخطوة تتألف من خطوتين استدعاء السكربت وإضافة التعليمات له. لنقم أولًا باستدعائه. في قالبك أضف مجلدًا جديدًا يسمى scirpts وفي داخله ملف فارغ باسم burger-menu-script.js. الآن افتح ملف functions.php في قالبك وأضف التالي إليه: function wpmu_burger_menu_scripts() { wp_enqueue_script( 'burger-menu-script', get_stylesheet_directory_uri() . '/scripts/burger-menu.js', array( 'jquery' ) ); } add_action( 'wp_enqueue_scripts', 'wpmu_burger_menu_scripts' ); /* الآن قمنا باستدعاء الملف بشكل صحيح وسنضيف التعليمات البرمجية له، افتح ملف وأضف التالي */ jQuery(document).ready(function() { jQuery('.toggle-nav').click(function(e) { jQuery('.menu.main ul').slideToggle(500); e.preventDefault(); }); }); هذه التعليمات استهدفت العنصر الذي له اسم الصنف toggle-nav. و هو في حالتنا الزر و الذي سوف يتم تنفيذ التعليمات عليه عند الضغط عليه من قبل المستخدم و ستعمل دالة slideToggle الخاصة بمكتبة jquery لإخفاء زر القائمة و إظهاره عند الضغط عليه من قبل المستخدم. والآن لنرى النتيجة: وعندما أضغط على الأيقونة تظهر القائمة: وهذا فيديو يوضح كيف تجري العملية: استخدام زر القائمة سيحسن تجربة المستخدم على جهاز الهاتف المحمول إذا اتبعت الخطوات السابقة، سيصبح لديك زر قائمة بسيط والذي سيحسن تجربة المستخدم بشكل كبير عندما يزور المستخدمون موقعك من شاشات صغيرة. ويمكنك فعل ما تشاء لتحسن مظهر هذا الزر وجعله مناسبًا لموقعك. ترجمة -وبتصرّف- للمقال How to Create a Custom Animated Burger Menu for WordPress لصاحبته Rachel McCollin حقوق الصورة البارزة محفوظة لـ Freepik
  3. منذ بدء استخدامنا لمفهوم التصميم المتجاوب في سير العمل الخاص بنا، ظهرت العديد من الأدوات كمحاولة لتسهيل عملنا وتسهيل تصميم المواقع المتجاوبة. ولكن مع ذلك فإنّه من الصعب عليك اختيار الأدوات المناسبة والتي سوف تفيدك حقًا في تطوير مشاريعك. وفي هذا المقال سوف أتحدث عن بعض الطرق التي طوّرتها لإنشاء نظام شبكي (grid) مرن وسلس يسمح بالتضمين (nesting) كما يعطيك الحرية الكاملة حول كيفية تصرّف هذا النظام الشبكي في كافة الأبعاد. كل هذا سوف يتم بمساعدة من مولّد النظام الشبكي المسمى Gridpak. الأمور التي سوف نتمكن من فعلهاعندما ننتهي من هذا الدرس سوف يكون لدينا نظام شبكي يمكنه فعل بعض مما يظهر في هذه المعاينة. ويمكن استخدامه في تنظيم تخطيط الصفحات كما في هذه المعاينة. متطلبات ما قبل البدءقبل أن نبدأ بمعرفة كيفية إنشاء هذا النظام الشبكي دعونا أولًا نقوم بتحديد ما نتوقعه من هذا النظام. هذه بعض النقاط التي أرى أنها مهمة عند استعمال أي نظام شبكي: الأعمدة دقيقة ويمكن توقعها لجميع أبعاد الأجهزة.التنسيقات والتوصيف (markup) يجب أن يكون بسيطًا وسهل الاستخدام.لا يعتمد على جهاز معين أو أبعاد معينة وأن يكون مرن بشكل كامل.قابل للتضمين (nestable) لأي عمق.مكون من وحدات (modular) وله سيطرة كاملة على الأعمدة في جميع الأبعاد.يجب أن يعمل من دون الحاجة لـ polyfill في المتصفحات القديمة.بالنسبة للنقطة الأخيرة، فكثير من المطورين يحبون استعمال respond.js لاستخدام ودعم الـmedia query في المتصفحات القديمة. الجيد في الأمر أنّ المتصفحات القديمة لا تتطلب أن يتم دعمها في أي جهاز سوى أجهزة سطح المكتب وأجهزة الحاسوب المحمول. لذلك سوف أقوم بتوضيح كيفية تحديد هذه المتصفحات وحفظ طلب http واحد. التنصيب1. وسم <html> ودعم تراجعي (fallback) لمتصفح IEسوف نحتاج في البداية إلى التأكد بأنّنا نستطيع تحديد المتصفحات القديمة وجعل تنسيقات CSS تعمل عليها. هذه الطريقة تم استيحاؤها من html5 boilerplate وتم توضيحها في هذه المقالة. <!doctype html> <!--[if IE 8]> <html class="no-js ie8 oldie" lang="en"> <![endif]--> <!--[if IE 9]> <html class="no-js ie9 oldie" lang="en"> <![endif]--> <!--[if gt IE 9]><!--> <html class="no-js" lang="en"> <!--<![endif]-->كل ما تفعله الشيفرة البرمجية في الأعلى هو إضافة فئة (class) إلى وسم <html> بناءً على المتصفح الذي يستخدمه الزائر. في هذا المقال سوف نقوم فقط بدعم متصفحات Internet Explorer بدءًا من الإصدار الثامن. وبهذا يمكننا أن نقوم بتحديد الإصدارات القديمة من متصفح Internet Explorer بالشكل التالي: /* target only IE8 with the following class */ .ie8 .my-class { ... } 2. التنسيقات الأساسية ووحدة Remإذا كنت لا تستخدم الوحدة rem في تطوير المواقع فإنني أنصحك بالقيام بذلك في أقرب وقت، فهي وحدة رائعة خصوصًا فيما يتعلق بأحجام الخطوط (font sizing). وهذه الوحدة رائعة لأنها وحدة نسبية ولكنها نسبية فقط إلى تعريف واحد وهو التعريف الذي يوضع في وسم <html>. وهذا يعني أنّ جميع الحسابات المعقدة التي تنتج عن استخدام الوحدة em سوف تختفي. أنظر إلى الشيفرة البرمجية التالية: html { ... font-size: 100%; ... } body { ... /* base font size: 16px (user agent dependant, but usually 16px) */ font-size: 1em; ... } ... .padding { /* IE8 fallback */ padding: 32px; padding: 2rem; /* 2 * 16px = 32px */ } بما أنّ متصفح IE8 لا يدعم وحدة rem فإننا نحتاج إلى استخدام وحدة px أيضًا. وسوف أريك لاحقًا كيف يمكن أن تساعدنا هذه الوحدة في تسهيل عملنا عند إنشاء الأنظمة الشبكية. 3. الحصول على Gridpakإذهب إلى موقع gridpak.com وحاول التلاعب بالخيارات حتى تحصل على ما يريحك. بالنسبة لي سوف أقوم بتحميل نظام شبكي بهذه الخيارات ولكن لك الحرية في اختيار ما تريده: الأبعاد: 480px، 640px، 960px وأعلى.12 عمود في جميع الأبعاد (12 عمود في الهواتف والأجهزة اللوحية وأجهزة سطح المكتب).padding بقيمة تساوي صفر.تباعد بين الأعمدة بقيمة (24px (24px gutter. يسمح لك Gridpak بأن تقوم بإنشاء gutters محددة لكل بعد من الأبعاد، ولكننا لن نقوم بذلك حتى تبقى الأمور بسيطة.حاول أن تكون قيم الـpadding والـgutters قيم ناتجة عن حسابات ضرب بسيطة بالنسبة لحجم الخط في الـ body. سوف يساعد هذا على تقليل القيم العشرية لأنّ المتصفحات تتعامل مع القيم العشرية بطريقة مختلفة وحتى نُبقي كل شيء أجمل. فعلى سبيل المثال لو كان الخط الأساسي هو 16px فإنّ التباعد بين الأعمدة يمكن أن يكون أحد القيم التالية: 8px (وهذه القيمة ناتجة عن ضرب 16 في 0.5).12px (وهذه القيمة ناتجة عن ضرب 16 في 0.75).24px (وهذه القيمة ناتجة عن ضرب 16 في 1.5).32px (وهذه القيمة ناتجة عن ضرب 16 في 2).بعد أن تقوم بتحديد جميع الخيارات قم بتحميل النظام الشبكي. 4. تفصيل الـ Gridpakمن أفضل خصائص Gridpak هو أنّك سوف تحصل على نظام شبكي دقيق ومتين فقط بعدة سطور من CSS، فقوة Gridpak تأتي من استعمال الخاصية box-sizing: border-box على جميع عناصر الموقع. ففي حالة استعمال border-box فإننا سوف نتمكن من الحصول على عمودين كل واحد منهما بعرض 50% وسوف يبقيان بجانب بعضهما وسوف تتمكن أيضًا من استيعاب أي قيمة border نعطيها له. ومع تطبيق بعض تنسيقات CSS يمكننا أن نُعطي الـgutters حواف (borders) شفافة مما يمنحنا أعمدة دقيقة ومناسبة: وفوق كل هذا فإنّ Gridpak يقوم بوضع كل نظام شبكي لكل بُعد داخل media query خاص به: /* * Declare a grid between 0 and 479px only */ @media screen and (min-width: 0px) and (max-width: 479px) { ... .span_1 { width:8.33333333333%;} .span_2 { width:16.6666666667%;} .span_3 { width:25.0%;} .span_4 { width:33.3333333333%;} .span_5 { width:41.6666666667%;} .span_6 { width:50.0%;} .span_7 { width:58.3333333333%;} .span_8 { width:66.6666666667%;} .span_9 { width:75.0%;} .span_10 { width:83.3333333333%;} .span_11 { width:91.6666666667%;} .span_12 { margin-left:0; width:100%; } } /* * Declare a grid between 480px and 639px only */ @media screen and (min-width: 480px) and (max-width: 639px) { ... } /* etc. */ ...إذا كان علينا أن نحدد عدد مختلف من الأعمدة لكل بُعد عندما نقوم باختيار الخيارات الخاصة بـGridpak (لنقل أنها عمودين للهواتف وأربعة للأجهزة اللوحية و12 لأجهزة سطح المكتب) فإنّ تعريفات media query سوف تمنحنا مرونة وسيطرة أكبر على الأعمدة بدل أن نقوم بجعل الهواتف والأجهزة اللوحية تأخذ عمود واحد فقط كما هو الحال في معظم الأنظمة الشبكية الأخرى. فعلى سبيل المثال يمكن لفئة (class) واحدة أن تجعل أحد الأعمدة يحتل العرض الكامل لصف ما (row) أو نصف العرض أو حتى سُدُسَ (1/6) العرض وذلك بناءً على بُعد المتصفح. كل هذا جيد ولكن هناك شيء ما وهو أننا نريد هذه الأعمدة أن تفعل ما أريده في جميع الأبعاد. 5. تطويع Gridpak كما نريداستعمال وحدة rem لتسهيل عملية الصيانةدعونا في البداية نقوم بأخذ أساس الـGridpak ونُعطيه إضافة حديثة (مع دعم المتصفحات القديمة): .row { margin-left: -24px; margin-left: -1.5rem; } /* Reusable column setup */ .col { border: 0px solid transparent; border-left-width: 24px; border-left-width: 1.5rem; float: left; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; -webkit-background-clip: padding-box !important; -moz-background-clip: padding-box !important; background-clip: padding-box !important; }إذا أردنا في المستقبل أن نزيل دعم المتصفح IE8 فيمكننا بكل سهولة فعل ذلك عن طريق إزالة القيم ذات الوحدة px (أي أن نُزيل الخاصية margin-left: -24px وborder-left-width: 24px). تكبير وتتابع النظام الشبكينريد أيضًا أن نتأكد بأنّ تخطيط الصفحة لا يصبح سيء عندما يقوم الزائر بتكبير محتوى الموقع. يمكنك قراءة هذا المقال حول استخدام الوحدات المتناسبة مع تعريفات الـmedia query. بطبيعة الحال فإنّ استخدام وحدة الـpixel في تعريفات الـmedia query لا يبدو وكأنّه يعمل بشكل جيد عندما يقوم الزائر بتكبير المحتوى لذلك سوف نستعمل وحدة em. زيادة على ذلك، فإنّ الـmedia queries تستخدم حجم الخط الأساسي المُعرّف في المتصفح (user agent font size)، وهذا يعني أننا -وبشكل عام- سوف نستخدم حجم الخط 16px في حساباتنا. دعونا نتخلّص من استعمال max-width في الـmedia query حتى نستغل قوة التتابع/التعاقب (cascade): /* * Declare a grid which will be effective from 0px */ @media screen and (min-width: 0) { ... } /* * Declare a grid which will be effective from 480px * 480 / 16 = 30em */ @media screen and (min-width: 30em) { ... } /* * Declare a grid which will be effective from 640px * 640 / 16 = 40em */ @media screen and (min-width: 40em) { ... } ...علاوة على إصلاح مشاكل التكبير، فإنّ هذا الأمر يسمح لعرض الأعمدة المُعرّفة داخل أبعاد أكبر بأن تقوم بإلغاء (override) تلك المعرفة لأبعاد أصغر. تسمية الأعمدة الخاصة ببُعد معينحتى نتمكن من السيطرة على الأعمدة في جميع الأبعاد بشكل أفضل فإننا سوف نحتاج إلى إعطائها أسماء فريدة حتى يحدث إلغاء للتنسيقات فقط إذا أردنا نحن ذلك. حتى أنّ متصفح IE8 سوف يحصل على تعريفات خاصة به. سوف أقوم بإضافة اسم في نهاية كل span لتحديد الأبعاد التي يعمل بها: /* * IE8 grid * This grid will only work in IE8, and at all widths */ .ie8 .span_1_ie8 { ... } .ie8 .span_2_ie8 { ... } ... .ie8 .span_1_ie8 { ... } /* * VIEWPORT ALPHA * Declare a grid which will be effective from 0px */ @media screen and (min-width: 0) { ... .span_1_vpalpha { ... } .span_2_vpalpha { ... } ... .span_1_vpalpha { ... } } /* * VIEWPORT BETA * Declare a grid which will be effective from 480px * 480 / 16 = 30em */ @media screen and (min-width: 20em) { ... .span_1_vpbeta { ... } .span_2_vpbeta { ... } ... .span_1_vpbeta { ... } } /* * VIEWPORT GAMMA * Declare a grid which will be effective from 640px * 640 / 16 = 40em */ ...لنبدأ الآن باستعمال النظام الشبكي. استعمال النظام الشبكيإنّ استعمال Gridpak لهو أمر في غاية السهولة، فجميع الأعمدة يجب أن تكون موجودة داخل عنصر حاوي وهذا العنصر يجب أن يملك فئة clearfix وهذ هو كل ما تحتاج معرفته. <!-- create a row with 2 columns and the following features: mobile -> 3 spans + 9 spans tablet -> 6 spans + 6 spans desktop -> 4 spans + 8 spans IE8 -> 4 spans + 8 spans --> <div class="row cf"> <div class="col span_3_vpalpha span_6_vpbeta span_4_vpgamma span_4_ie8"> content... </div> <div class="col span_9_vpalpha span_6_vpbeta span_8_vpgamma span_8_ie8"> content... </div> </div>تضمين الأعمدة يتبع نفس النمط: <!-- create a row with 2 columns spanning half the width for all viewports. Have both columns then contain 2 columns each, both spanning half the width of the parent column. --> <div class="row cf"> <div class="col span_6_vpalpha span_6_ie8"> <div class="row cf"> <div class="col span_6_vpalpha span_6_ie8"> content... </div> <div class="col span_6_vpalpha span_6_ie8"> content... </div> </div> </div> <div class="col span_6_vpalpha span_6_ie8"> <div class="row cf"> <div class="col span_6_vpalpha span_6_ie8"> content... </div> <div class="col span_6_vpalpha span_6_ie8"> content... </div> </div> </div> </div>قد تقول في نفسك أنّ هناك العديد من الفئات التي يلزم استخدامها، وقد يكون ذلك صحيحًا ولكني متأكد أنّك لن تجد نظام شبكي بهذه المرونة فقط باستعمال بعض الفئات. خاتمةلقد كانت رحلة طويلة احتوت على العديد من الأمور والأفكار المفيدة، وأتمنى أن تكون قد استفدت من هذا الدرس. ترجمة -وبتصرّف- للمقال Creating Nestable Dynamic Grids لصاحبه Larry Botha.
  4. يُعتبر مربع البحث أحد المكونات التي لا غنى عنها في أي موقع، وفي هذا الدرس سوف تتعلم كيف يمكنك إنشاء مربع بحث يظهر ويختفي عند النقر عليه. قد تعتقد أنّ القيام بذلك سيكون سهلًا جدًا ولكنك سوف ترى أنّ الأمر ليس كذلك، فسوف نحتاج إلى تطبيق بعض الخدع حتى نجعل كل شيء يعمل بشكل صحيح. هذه هي المتطلبات التي نريدها مبدئيًا: ما نريده مبدئيًا هو ظهور زر يحتوي على أيقونة بحث.عند النقر على ذلك الزر نريد أن يظهر مربع البحث.نريده أن يكون متجاوبًا مع جميع الأجهزة.عند كتابة أي شيء في مربع البحث نريد أن يقوم المربع بالقيام بالبحث فور النقر على مفتاح الدخول (enter) من لوحة المفاتيح أو النقر على الأيقونة نفسها.إذا كان مربع البحث ظاهرًا ولكنه كان فارغًا فإننا نريده أن يختفي عند النقر على أيقونة البحث (أي أننا إذا قمنا بالنقر على أيقونة البحث وكان المربع فارغًا فإننا لا نريده أن يقوم بالبحث وإنما يقوم بإخفاء مربع البحث).نريد أيضًا أن يختفي مربع البحث عند النقر خارجه سواء كان فارغًا أم لا.لو كانت الجافاسكربت معطلة لدى المستخدم فإننا نريد لمربع البحث أن يكون ظاهرًا (أي دون الحاجة إلى النقر على أيقونة البحث).نريد أيضًا أن ندعم الأجهزة التي تعمل باللمس.يمكنك معاينة النتيجة النهائية لهذا الدرس. بما أننا بتنا الآن نعرف ما نريد فلنقم بذلك. بنية ملف HTMLكل ما نحتاجه من وسوم HTML هو حاوٍ رئيسي (main container) وسوف يكون عبارة عن وسم <div> وسوف نحتاج إلى وسم <form> وإلى حقلي إدخال (inputs)؛ واحد من نوع "text" والآخر من نوع "submit" وأخيرًا سوف نحتاج إلى وسم <span> ليحتوي على أيقونة البحث: <div id="sb-search" class="sb-search"> <form> <input class="sb-search-input" placeholder="Enter your search term..." type="search" value="" name="search" id="search"> <input class="sb-search-submit" type="submit" value=""> <span class="sb-icon-search"></span> </form> </div>لنبدأ الآن بتنسيق العناصر باستخدام CSS. تنسيقات CSSبناءً على المتطلبات التي ذكرناها سابقًا فإنه يجب في البداية أن يكون لدينا زر يحتوي على أيقونة بحث وباقي العناصر يجب أن تكون مخفية. دعونا الآن نتخيل ما الذي سيحصل عند تمدد مربع البحث وجعله ظاهرًا (الذي هو نفسه سيكون الحاوي الرئيسي الذي ذكرناه سابقًا). كيف نقوم بذلك؟ سوف نستخدم الخاصية overflow: hidden وتكبير العرض الخاص بالعنصر الحاوي (sb-search) يجب أن يقوم بإظهار حقل البحث. إذًا أول شيء نقوم به هو تنسيق العنصر الحاوي (sb-search) بحيث سوف نجعله يطوف إلى اليمين باستخدام الخاصية float: right ونعطيه الخاصية overflow: hidden، والعرض يجب أن يكون 60px ولكن بما أننا نريد أن يزيد العرض إلى 100% فإننا سنواجه بعض المشاكل في متصفحات iOS فهي لا تقبل التغيير من عرض يعتمد على الـpixels إلى عرض يعتمد على النسب المئوية. لذلك سوف نقوم بتعريف خاصية min-width بالقيمة 60px وخاصية width بقيمة 0%. يمكنك قراءة المزيد عن هذا الحل العبقري من خلال هذا الرابط. سوف نستعمل أيضًا الخاصية transition والخاصية webkit-backface-visibility: hidden- لتلافي بعض الآثار للحقول في متصفحات الهواتف iOS: .sb-search { position: relative; margin-top: 10px; width: 0%; min-width: 60px; height: 60px; float: right; overflow: hidden; -webkit-transition: width 0.3s; -moz-transition: width 0.3s; transition: width 0.3s; -webkit-backface-visibility: hidden; }أي شيء يتجاوز/يفيض عن هذا المربع الصغير لن يكون ظاهرًا. لنقم الآن بموضعة حقل البحث. سوف نستعمل قيمة مئوية بالنسبة للعرض حتى نسمح للحقل بأن يتمدد مع تمدد العنصر الحاوي. ومع إضافة الارتفاع (height) وحجم الخط (font-size) والـpadding المناسبة يمكننا توسيط النص داخل العنصر (استعملنا padding بدل line-height لأن الخاصية line-height لا تعمل بشكل جيد في متصفح IE8). قد يبدو استعمالنا للخاصية position: absolute غير ضروري، ولكن استعمالها يقوم بحل مشكلة تظهر عند إغلاق مربع البحث بحيث يبدو الحقل وكأنه ظاهر في الجانب الأيمن لفترة قصيرة جدًا. .sb-search-input { position: absolute; top: 0; right: 0; border: none; outline: none; background: #fff; width: 100%; height: 60px; margin: 0; z-index: 10; padding: 20px 65px 20px 20px; font-family: inherit; font-size: 20px; color: #2c3e50; } input[type="search"].sb-search-input { -webkit-appearance: none; -webkit-border-radius: 0px; }قمنا أيضًا بإزالة التنسيقات الإفتراضية لحقل البحث في متصفحات WebKit. دعونا نقوم الآن بتعريف لون الخط بالنسبة للـplaceholder (الـplaceholder هو نص يظهر داخل حقل البحث قبل أن يقوم المستخدم بكتابة أي شيء بداخله حتى يُعطي المستخدم لمحة عما يجب عليه كتابته في ذلك الحقل): .sb-search-input::-webkit-input-placeholder { color: #efb480; } .sb-search-input:-moz-placeholder { color: #efb480; } .sb-search-input::-moz-placeholder { color: #efb480; } .sb-search-input:-ms-input-placeholder { color: #efb480; }دعونا الآن نقوم بتنسيق زر أيقونة البحث وحقل التأكيد/الإرسال (submit input)، فنحن نريدهما أن يظهرا في نفس المكان لذلك يجب أن نضعهما في الجانب الأيمن ونعطيهما نفس الأبعاد. وبما أنهما سيظهران فوق بعضهما فسوف نعطيهما الخاصية position: absolute: .sb-icon-search, .sb-search-submit { width: 60px; height: 60px; display: block; position: absolute; right: 0; top: 0; padding: 0; margin: 0; line-height: 60px; text-align: center; cursor: pointer; }نريد في بداية الأمر أن تكون الأيقونة قابلة للنقر، وعند فتح حقل البحث فإننا نريد لحقل التأكيد/الإرسال (submit input) أن يكون قابلًا للنقر. لذلك سوف نُعطي حقل التأكيد الخاصية z-index: -1 ونجعله شفافًا/مخفيًا حتى يمكننا رؤية أيقونة البحث: .sb-search-submit { background: #fff; /* IE needs this */ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; /* IE 8 */ filter: alpha(opacity=0); /* IE 5-7 */ opacity: 0; color: transparent; border: none; outline: none; z-index: -1; }لاحظ أننا لم نقم بجعل الخلفية شفافة وذلك لأن الأمر لا يعمل بشكل جيد في متصفح IE فالعنصر لا يكون قابلًا للنقر عند ذلك، لذلك قمنا باستعمال لون معين (الأبيض) وجعلنا الشفافية (opacity) تساوي صفر. وبما أننا نريد لأيقونة البحث أن تظهر فوق كل شيء فإننا سوف نقوم بإعطائها الخاصية z-index بقيمة عالية، وسوف نقوم باستخدام الفئة الزائفة ::before لإضافة الأيقونة: .sb-icon-search { color: #fff; background: #e67e22; z-index: 90; font-size: 22px; font-family: 'icomoon'; speak: none; font-style: normal; font-weight: normal; font-variant: normal; text-transform: none; -webkit-font-smoothing: antialiased; } .sb-icon-search:before { content: "\e000"; }ودعونا لا ننسى إضافة خط الويب (web font) في بداية ملف الـCSS: /* Search icon by IcoMoon, made with http://icomoon.io/app/ */ @font-face { font-family: 'icomoon'; src:url('../fonts/icomoon/icomoon.eot'); src:url('../fonts/icomoon/icomoon.eot?#iefix') format('embedded-opentype'), url('../fonts/icomoon/icomoon.woff') format('woff'), url('../fonts/icomoon/icomoon.ttf') format('truetype'), url('../fonts/icomoon/icomoon.svg#icomoon') format('svg'); font-weight: normal; font-style: normal; }يمكننا الآن إعطاء العنصر الحاوي (sb-search) عرضًا بقيمة 100% وذلك في حالتين؛ الأولى عند النقر على أيقونة البحث وذلك عن طريق إضافة فئة (class) بالاسم "sb-search-open" باستخدام الجافاسكربت والثانية عندما لا يكون الجافاسكربت مفعّلًا لدى المستخدم: .sb-search.sb-search-open, .no-js .sb-search { width: 100%; }لنقم الآن بتغيير لون الخط الخاص بعنصر أيقونة البحث ونضعه أسفل حقل التأكيد/الإرسال وذلك عن طريق إعطائه قيمة z-index أقل من 90 (وهي القيمة التي أعطيناها للعنصر "sb-icon-search"): .sb-search.sb-search-open .sb-icon-search, .no-js .sb-search .sb-icon-search { background: #da6d0d; color: #fff; z-index: 11; }وأخيرًا لنقم بإضافة الخاصية z-index لحقل التأكيد/الإرسال ولكن بقيمة أكبر من 11 حتى يمكننا النقر عليها: .sb-search.sb-search-open .sb-search-submit, .no-js .sb-search .sb-search-submit { z-index: 90; }انتهينا الآن من تنسيقات CSS وبقي علينا الجافاسكربت. بعض الجافاسكربتلنبدأ بإضافة وإزالة الفئة "sb-search-open". بحيث سوف يتم إضافة الفئة عند النقر على الحاوي الرئيسي (sb-search) وإزالته عند النقر على حقل التأكيد/الإرسال ولكن فقط إذا كان حقل البحث فارغًا، أمّا إذا لم يكن فارغًا فإننا نريد تأكيد عملية البحث. وحتى لا نقوم بإزالة الفئة عند النقر على الحقل (لأن الحاوي بأكمله قابل للنقر) فإننا نحتاج إلى منع حدث النقر (click event) من الانتشار على ذلك العنصر. هذا يعني أنّ النقر على الحقل لن يؤدي إلى إثارة حدث النقر على العناصر الحاوية له. ;( function( window ) { function UISearch( el, options ) { this.el = el; this.inputEl = el.querySelector( 'form > input.sb-search-input' ); this._initEvents(); } UISearch.prototype = { _initEvents : function() { var self = this, initSearchFn = function( ev ) { if( !classie.has( self.el, 'sb-search-open' ) ) { ev.preventDefault(); self.open(); } else if( classie.has( self.el, 'sb-search-open' ) && /^\s*$/.test( self.inputEl.value ) ) { self.close(); } } this.el.addEventListener( 'click', initSearchFn ); this.inputEl.addEventListener( 'click', function( ev ) { ev.stopPropagation(); }); }, open : function() { classie.add( this.el, 'sb-search-open' ); }, close : function() { classie.remove( this.el, 'sb-search-open' ); } } window.UISearch = UISearch; } )( window );سوف نحتاج أيضًا إلى إضافة الأحداث التي تقوم بإزالة الفئة "sb-search-open" عند النقر خارج مربع البحث، وحتى يعمل ذلك فإننا نريد أن نتعامل مع انتشار الأحداث (event bubbling) عند النقر على الحاوي الرئيسي. ;( function( window ) { function UISearch( el, options ) { this.el = el; this.inputEl = el.querySelector( 'form > input.sb-search-input' ); this._initEvents(); } UISearch.prototype = { _initEvents : function() { var self = this, initSearchFn = function( ev ) { ev.stopPropagation(); if( !classie.has( self.el, 'sb-search-open' ) ) { ev.preventDefault(); self.open(); } else if( classie.has( self.el, 'sb-search-open' ) && /^\s*$/.test( self.inputEl.value ) ) { self.close(); } } this.el.addEventListener( 'click', initSearchFn ); this.inputEl.addEventListener( 'click', function( ev ) { ev.stopPropagation(); }); }, open : function() { var self = this; classie.add( this.el, 'sb-search-open' ); // close the search input if body is clicked var bodyFn = function( ev ) { self.close(); this.removeEventListener( 'click', bodyFn ); }; document.addEventListener( 'click', bodyFn ); }, close : function() { classie.remove( this.el, 'sb-search-open' ); } } window.UISearch = UISearch; } )( window );وشيء آخر علينا الاهتمام به وهو قصّ مصطلح البحث. عندما نقوم أيضًا بالنقر على أيقونة البحث فإننا نريد أن يكون الحقل مفعّلًا (focused)، ولأنّ هذا يسبب بعض المشاكل في متصفح iOS (لوحة المفاتيح تظهر في نفس الوقت) فإننا نريد تلافي ذلك في هذه الحالة، وعندما يتم إغلاق مربع البحث فإننا نريد أن يكون مربع البحث غير مفعل (blur). هذا سوف يحل بعض المشاكل في بعض الأجهزة التي تُظهر بأنّ المؤشر يومض حتى بعد أن يكون الحقل مغلقًا. ملاحظة جانبية: كلمة focus تدل على أنّ مربع البحث مفعّل (أي أنّ المؤشر بداخله) وكلمة blur تدل على عكس ذلك. ;( function( window ) { // http://stackoverflow.com/a/11381730/989439 function mobilecheck() { var check = false; (function(a){if(/(android|ipad|playbook|silk|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera); return check; } // http://www.jonathantneal.com/blog/polyfills-and-prototypes/ !String.prototype.trim && (String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g, ''); }); function UISearch( el, options ) { this.el = el; this.inputEl = el.querySelector( 'form > input.sb-search-input' ); this._initEvents(); } UISearch.prototype = { _initEvents : function() { var self = this, initSearchFn = function( ev ) { ev.stopPropagation(); // trim its value self.inputEl.value = self.inputEl.value.trim(); if( !classie.has( self.el, 'sb-search-open' ) ) { ev.preventDefault(); self.open(); } else if( classie.has( self.el, 'sb-search-open' ) && /^\s*$/.test( self.inputEl.value ) ) { self.close(); } } this.el.addEventListener( 'click', initSearchFn ); this.inputEl.addEventListener( 'click', function( ev ) { ev.stopPropagation(); }); }, open : function() { var self = this; classie.add( this.el, 'sb-search-open' ); // focus the input if( !mobilecheck() ) { this.inputEl.focus(); } // close the search input if body is clicked var bodyFn = function( ev ) { self.close(); this.removeEventListener( 'click', bodyFn ); }; document.addEventListener( 'click', bodyFn ); }, close : function() { this.inputEl.blur(); classie.remove( this.el, 'sb-search-open' ); } } window.UISearch = UISearch; } )( window );وحتى يعمل كل شيء بشكل سلس في أجهزة الهواتف فإننا سوف نحتاج إلى إضافة أحداث اللمس (touch events). كما أنّ إضافة preventDefault في دالّة initSearchFn سوف يمنع حدث النقر واللمس من أن يتفعّلا مع بعضهما في أجهزة اللمس. ;( function( window ) { // http://stackoverflow.com/a/11381730/989439 function mobilecheck() { var check = false; (function(a){if(/(android|ipad|playbook|silk|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera); return check; } // http://www.jonathantneal.com/blog/polyfills-and-prototypes/ !String.prototype.trim && (String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g, ''); }); function UISearch( el, options ) { this.el = el; this.inputEl = el.querySelector( 'form > input.sb-search-input' ); this._initEvents(); } UISearch.prototype = { _initEvents : function() { var self = this, initSearchFn = function( ev ) { ev.stopPropagation(); // trim its value self.inputEl.value = self.inputEl.value.trim(); if( !classie.has( self.el, 'sb-search-open' ) ) { ev.preventDefault(); self.open(); } else if( classie.has( self.el, 'sb-search-open' ) && /^\s*$/.test( self.inputEl.value ) ) { ev.preventDefault(); self.close(); } } this.el.addEventListener( 'click', initSearchFn ); this.el.addEventListener( 'touchstart', initSearchFn ); this.inputEl.addEventListener( 'click', function( ev ) { ev.stopPropagation(); }); this.inputEl.addEventListener( 'touchstart', function( ev ) { ev.stopPropagation(); } ); }, open : function() { var self = this; classie.add( this.el, 'sb-search-open' ); // focus the input if( !mobilecheck() ) { this.inputEl.focus(); } // close the search input if body is clicked var bodyFn = function( ev ) { self.close(); this.removeEventListener( 'click', bodyFn ); this.removeEventListener( 'touchstart', bodyFn ); }; document.addEventListener( 'click', bodyFn ); document.addEventListener( 'touchstart', bodyFn ); }, close : function() { this.inputEl.blur(); classie.remove( this.el, 'sb-search-open' ); } } window.UISearch = UISearch; } )( window );وأخيرًا، للأجهزة التي لا تدعم addEventListener وremoveEventListener فإننا سوف نستعمل polyfill. // EventListener | @jon_neal | //github.com/jonathantneal/EventListener !window.addEventListener && window.Element && (function () { function addToPrototype(name, method) { Window.prototype[name] = HTMLDocument.prototype[name] = Element.prototype[name] = method; } var registry = []; addToPrototype("addEventListener", function (type, listener) { var target = this; registry.unshift({ __listener: function (event) { event.currentTarget = target; event.pageX = event.clientX + document.documentElement.scrollLeft; event.pageY = event.clientY + document.documentElement.scrollTop; event.preventDefault = function () { event.returnValue = false }; event.relatedTarget = event.fromElement || null; event.stopPropagation = function () { event.cancelBubble = true }; event.relatedTarget = event.fromElement || null; event.target = event.srcElement || target; event.timeStamp = +new Date; listener.call(target, event); }, listener: listener, target: target, type: type }); this.attachEvent("on" + type, registry[0].__listener); }); addToPrototype("removeEventListener", function (type, listener) { for (var index = 0, length = registry.length; index < length; ++index) { if (registry[index].target == this && registry[index].type == type && registry[index].listener == listener) { return this.detachEvent("on" + type, registry.splice(index, 1)[0].__listener); } } }); addToPrototype("dispatchEvent", function (eventObject) { try { return this.fireEvent("on" + eventObject.type, eventObject); } catch (error) { for (var index = 0, length = registry.length; index < length; ++index) { if (registry[index].target == this && registry[index].type == eventObject.type) { registry[index].call(this, eventObject); } } } }); })();خاتمةهذا كان كل شيء فيما يخص هذا الدرس، أتمنى أن تكون قد استفدت منه وتعلمت منه شيئًا جديدًا. ترجمة -وبتصرّف- للمقال Expanding Search Bar Deconstructed لصاحبته Mary Lou.
  5. هل قمت من قبل باستعمال Media Queries CSS لإنشاء موقع متجاوب؟ إن كان جوابك لا فأعتقد أنه قد حان الوقت لذلك. ففي هذا الدرس سنقوم بتحويل قالب ووردبريس غير متجاوب إلى قالب متجاوب. في مقالات سابقة قمنا بتصميم، تكويد وبناء قالب ووردبريس كامل. التصميم نفسه قابل لأن يتم تحويله إلى تصميم متجاوب، لذلك سوف نستعمل نفس ملفات HTML وCSS لتحويل التصميم الثابت إلى آخر متجاوب. قبل أن نقوم بأي شيء أريدك أن تذهب لمعاينة النسخة الرئيسية من القالب قبل أن يصبح متجاوب وقم بتقليص حجم المتصفح ولاحظ ما يحصل. هل رأيت كيف يختفي جزء من الموقع عندما يصبح عرض المتصفح أقل من عرض الموقع نفسه وكيف يظهر شريط للتمرير (scrollbar) أسفل المتصفح؟ هذا ما يحدث للمواقع عندما لا تكون متجاوبة، فالفكرة من التصميم المتجاوب هو أن يتجاوب الموقع مع جميع أبعاد المتصفحات وأن يظهر الموقع بأفضل شكل مهما كانت أبعاد المتصفح الذي سيعرض الموقع. عند تصفح المواقع غير المتجاوبة على الأجهزة الذكية واللوحية فإن تلك الأجهزة تقوم تلقائيًا بعرض الموقع كاملًا (أي أنّه لن يختفي جزء من الموقع كما رأيت مسبقًا)، ولكنّك ستحتاج إلى التكبير(zooming) حتى ترى محتويات الموقع بشكل جيد وهذا ما لا نريده. ما نريده هو أن يتم عرض المواقع على الأجهزة ذات الأبعاد الصغيرة بشكل مناسب ومن دون الحاجة إلى التكبير(zooming). أول خطوة يجب القيام بها هي أن نقرر الأبعاد التي سوف نصمم لها، فهناك الكثير من الأبعاد والأحجام ولكن تعتبر الأبعاد 320px، 768px و1024px من الأبعاد الشائعة. ومن الجيد أيضًا أن تعلم بأنّ التصاميم التي تعتمد في تجاوبها على عدد معين من الأبعاد تسمى "adaptive"، أما تلك التي نستخدم فيها قيم em والنسب المئوية والتي لا تعتمد على عدد معين من الأبعاد فتسمى "responsive". <link href="mediaqueries.css" rel="stylesheet" />يمكننا الآن البدء باستعمال CSS media queries لجعل الموقع متجاوبًا. يمكنك إضافة الـmedia queries إلى ملف CSS الرئيسي ولكني أفضل أن أضعها بملفٍ مستقل وأن أربطها بملف الـHTML باستخدام الوسم `<link>` (كما هو موضح في الأعلى). <meta name="viewport" content="width=device-width; initial-scale=1.0">نريد أيضًا أن نمنع أجهزة الـiPhone من أن تقوم بعرض الموقع على كامل الشاشة مما يضطرنا إلى التكبير لرؤية المحتوى، لذلك سنقوم باستعمال وسم `<meta>` لإخبار متصفح Safari بأن يكون عرض الموقع هو نفسه عرض الجهاز. @media screen and (max-width: 960px) { ... } العرض الخاص بالتصميم الأصلي هو 960px ولذلك فإنّ أي عرض لمتصفح أقل من هذا العرض سوف يؤدي إلى إخفاء جزء من المحتوى وإلى ظهور أشرطة تمرير أسفل المتصفح (كما رأيت سابقًا في بداية الدرس). وبناءً على ذلك، فسيكون أول media query نقوم بوضعها هي للشاشات التي عرضها أقل من 960px: @media screen and (max-width: 960px) { #container, footer { width: 758px; } #content { margin: 0 20px 0 0; } #sidebar { width: 212px; } #sidebar section { clear: left; } #sidebar #search #searchbar { width: 152px; } } هناك عرض آخر أقل من 960px وهو 768px وهو أحد الأبعاد الشائعة كما ذكرنا آنفًا (تجد هذا العرض شائعًا في الوضع الطولي(portrait) للأجهزة اللوحية). لقد قلنا أننا سنقوم بتحويل قالب ووردبريس تم تصميمه سابقًا، وبما أنّ القالب قد تمّ تصميمه بناءً على grid ما وحتى نتماشى مع تقسيم الصفحة(layout) فسوف نقوم بإزالة عمود(column) ليبقى لدينا 758px. يمكن عندها للتقسيم الأصلي للصفحة أن يتقلص وذلك عن طريق تقليل قيمة الـmargin الخاصة بـdiv المحتوى (#content) وتقليل العرض الكُلّي للقائمة الجانبية(sidebar). @media screen and (max-width: 758px) { #container, footer, #sidebar { width: 524px; } header nav { clear: left; float: none; overflow: hidden; } header nav li { width: auto; margin: 0 25px 0 0; } header { margin: 0 0 44px 0; } header h1 { margin: 0 0 24px 0; } #sidebar section { float: left; clear: none; } #sidebar #social { margin: 0 20px 47px 0; } #sidebar #search #searchbar { width: 464px; } } يمكننا الآن استخدام قيمة 758px للـmedia query التالية، بحيث يتم الإنتقال إلى التقسيم التالي عندما يكون حجم المتصفح أقل من هذه القيمة، وبما أنّ عرض القائمة الجانبية عند هذه النقطة سيكون ضيّقًا ولن يكون بالإمكان جعله أضيق من ذلك، فسوف نجعله ينساب أسفل المحتوى الرئيسي، وهذا يعني بأنَّ عرض القائمة الجانبية يجب أن يزيد حتى يملأ عرض الصفحة إلى أقصى درجة ممكنة، وسوف نجعل العناصر الموجودة في هذه القائمة تنساب إلى جانب بعضها(floated) لتملأ المساحة المتبقية. أصبحت الترويسة(header) ضيّقة أيضًا ولن تستطيع استيعاب وجود الشّعار وعناصر القائمة الرئيسية إلى جانب بعضها البعض، لذلك سوف نجعل عناصر القائمة الرئيسية تظهر على سطر جديد لوحدها. @media screen and (max-width: 524px) { #container, footer, #sidebar, #content { width: 292px; } #content article h2 { font-size: 24px; } #content .postinfo li { margin: 0 10px 0 0; } #sidebar #social { margin: 0; } #sidebar #search #searchbar { width: 230px; } } بقي الآن آخر media query وهي التي سوف تكون مخصصة للشاشات والأبعاد الصغيرة جدًا (كالأجهزة الذكية على سبيل المثال(. سيكون عرض هذه التقسيمة أصغر من العرض الأصلي للمحتوى، وبذلك سوف نحتاج لتقليص العرض حتى يتناسب مع تلك الأبعاد. لقد أدّى العرض الصغير جدًا إلى إنزال الروابط الخاصة بالتدوينة على سطر جديد، ولكن يمكننا إصلاح الأمر وذلك بتقليل المسافة بينها (تقليل قيمة الـmargin). وبهذا نكون قد حصلنا على تصميم يمكنه التجاوب مع العديد من الأحجام والأبعاد الشائعة وسوف يساعد أيضًا على زيادة قابلية القراءة للمحتوى بدل أن يضطر الزائر إلى التمرير(scroll) أو التكبير حتى يستطيع قراءة المحتوى. يمكنك الإطلاع على الموقع المتجاوب من هنا. ترجمة -وبتصرّف- للمقال Create a Responsive Web Design with Media Queries لصاحبه Iggy.