تنسيق النماذج والمدخلات
لماذا يعد تنسيق النماذج أمرا صعبا
تعد النماذج من أصعب العناصر التي يمكن تنسيقها بشكل متسق في CSS. على عكس معظم عناصر HTML التي يتم عرضها بالكامل بواسطة محرك CSS في المتصفح، يتم عرض عناصر التحكم في النماذج مثل حقول الإدخال والقوائم المنسدلة ومربعات الاختيار وأزرار الراديو ومدخلات الملفات باستخدام عناصر واجهة نظام التشغيل الأصلية. هذا يعني أن مربع الاختيار على macOS يبدو مختلفا عن مربع الاختيار على Windows، والقائمة المنسدلة في Chrome تبدو مختلفة عنها في Firefox -- حتى على نفس نظام التشغيل. يقوم المتصفح بشكل أساسي بتفويض عرض هذه العناصر لمجموعة أدوات واجهة المستخدم الخاصة بنظام التشغيل، ولهذا السبب قاومت تاريخيا تنسيق CSS.
هذا يمثل مشكلة حقيقية لمطوري الويب الذين يريدون تجربة متسقة وذات علامة تجارية عبر جميع المنصات. سيبدو تصميم النموذج الجميل من المصمم مختلفا على كل متصفح ونظام تشغيل ما لم تتخذ خطوات محددة لتجاوز العرض الأصلي. الخبر السار هو أن CSS الحديث يوفر أدوات قوية للتحكم الكامل في تنسيق النماذج، من خاصية appearance التي تزيل التنسيق الأصلي إلى العناصر الزائفة والفئات الزائفة التي تتيح لك تنسيق كل حالة ومكون فرعي من عناصر التحكم.
في هذا الدرس، سنبني تدريجيا من تنسيق المدخلات الأساسية إلى مربعات اختيار مخصصة بالكامل وأزرار راديو ومفاتيح تبديل ومنزلقات نطاق ونماذج محققة. بنهاية الدرس، سيكون لديك مجموعة أدوات كاملة لإنشاء نماذج جميلة وسهلة الوصول ومتسقة عبر جميع المتصفحات.
إعادة تعيين الأنماط الأصلية باستخدام appearance: none
الخطوة الأولى في تنسيق النماذج المخصصة هي إزالة مظهر نظام التشغيل الأصلي من عناصر التحكم. تتحكم خاصية appearance فيما إذا كان العنصر يُعرض باستخدام التنسيق الأصلي للمنصة أو تنسيق CSS العادي. تعيين appearance: none يخبر المتصفح بالتوقف عن استخدام عرض العنصر الأصلي وبدلا من ذلك يعامل العنصر كصندوق عادي يمكنك تنسيقه باستخدام CSS.
إعادة تعيين مظهر عناصر التحكم في النماذج
/* إعادة تعيين المظهر على عناصر التحكم الشائعة */
input[type="text"],
input[type="email"],
input[type="password"],
input[type="number"],
input[type="tel"],
input[type="url"],
input[type="search"],
input[type="date"],
textarea,
select {
appearance: none;
-webkit-appearance: none; /* احتياطي لـ Safari */
border: 1px solid #ccc;
border-radius: 4px;
padding: 8px 12px;
font-family: inherit;
font-size: inherit;
line-height: 1.5;
color: inherit;
background-color: #fff;
}
/* إعادة تعيين مربعات الاختيار وأزرار الراديو */
input[type="checkbox"],
input[type="radio"] {
appearance: none;
-webkit-appearance: none;
width: 20px;
height: 20px;
border: 2px solid #ccc;
background-color: #fff;
cursor: pointer;
}
input[type="checkbox"] {
border-radius: 4px;
}
input[type="radio"] {
border-radius: 50%;
}
font-family: inherit وfont-size: inherit مهمة. بشكل افتراضي، لا ترث عناصر التحكم في النماذج الخط من عناصرها الأم -- تستخدم الخط الافتراضي لنظام التشغيل. إضافة inherit تضمن أن عناصر التحكم في النماذج تطابق الطباعة في بقية صفحتك.تنسيق حقول النص
حقول النص هي أكثر عناصر النماذج شيوعا والأسهل في التنسيق بمجرد إعادة تعيين المظهر الأصلي. المفتاح لتنسيق حقول نص جيدة يكمن في ضبط الحشوة والحدود وحالات التركيز بشكل صحيح. دعنا نبني تنسيق حقل نص شامل يغطي جميع التفاصيل المهمة.
تنسيق شامل لحقول النص
/* أنماط الإدخال الأساسية */
.form-input {
display: block;
width: 100%;
padding: 10px 14px;
font-family: inherit;
font-size: 1rem;
line-height: 1.5;
color: #1a1a2e;
background-color: #fff;
border: 2px solid #d1d5db;
border-radius: 8px;
outline: none;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
box-sizing: border-box;
}
/* حالة التمرير */
.form-input:hover {
border-color: #9ca3af;
}
/* حالة التركيز مع حلقة مرئية */
.form-input:focus {
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.25);
}
/* تنسيق نص العنصر النائب */
.form-input::placeholder {
color: #9ca3af;
opacity: 1; /* Firefox يعين الشفافية لأقل من 1 افتراضيا */
}
/* حقل إدخال بأيقونة داخلية */
.input-with-icon {
position: relative;
}
.input-with-icon .form-input {
padding-left: 40px;
}
.input-with-icon .icon {
position: absolute;
left: 12px;
top: 50%;
transform: translateY(-50%);
color: #9ca3af;
pointer-events: none;
}
/* متغيرات صغيرة وكبيرة */
.form-input--sm {
padding: 6px 10px;
font-size: 0.875rem;
border-radius: 6px;
}
.form-input--lg {
padding: 14px 18px;
font-size: 1.125rem;
border-radius: 10px;
}
تنسيق التركيز باستخدام :focus و :focus-visible
تنسيق التركيز أمر حاسم لإمكانية الوصول. عندما يتنقل المستخدم عبر النموذج باستخدام لوحة المفاتيح، يحتاج إلى مؤشر مرئي واضح للعنصر المركّز حاليا. ومع ذلك، غالبا لا تريد إظهار أنماط التركيز عندما ينقر المستخدم على حقل إدخال بالماوس، لأن النقرة نفسها توضح أي عنصر نشط. هنا يأتي دور :focus-visible.
الفئة الزائفة :focus تتطابق عندما يكون للعنصر تركيز، بغض النظر عن كيفية حصوله عليه (لوحة المفاتيح أو الماوس أو برمجيا). الفئة الزائفة :focus-visible تتطابق فقط عندما يحدد المتصفح أن التركيز يجب أن يُشار إليه بصريا -- عادة عندما يتنقل المستخدم بلوحة المفاتيح. هذا يسمح لك بإظهار حلقة تركيز بارزة لمستخدمي لوحة المفاتيح مع الحفاظ على الواجهة نظيفة لمستخدمي الماوس.
Focus مقابل Focus-Visible
/* إزالة المخطط الافتراضي لجميع حالات التركيز */
.form-input:focus {
outline: none;
}
/* تنسيق دقيق لجميع حالات التركيز (الماوس ولوحة المفاتيح) */
.form-input:focus {
border-color: #3b82f6;
}
/* حلقة بارزة فقط للتنقل بلوحة المفاتيح */
.form-input:focus-visible {
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.4);
}
/* يمكنك أيضا استخدام :focus:not(:focus-visible) للتركيز بالماوس فقط */
.form-input:focus:not(:focus-visible) {
box-shadow: none; /* بدون حلقة عند النقر بالماوس */
}
/* زر مخصص مع مؤشر تركيز واضح للوحة المفاتيح */
.form-button:focus-visible {
outline: 3px solid #3b82f6;
outline-offset: 2px;
}
outline: none على جميع العناصر دون استبدال مؤشر التركيز يجعل نموذجك غير قابل للوصول لمستخدمي لوحة المفاتيح. إذا أزلت المخطط الافتراضي، استبدله دائما بتنسيق تركيز مخصص باستخدام border-color أو box-shadow أو outline مخصص.تنسيق العنصر النائب باستخدام ::placeholder
العنصر الزائف ::placeholder يتيح لك تنسيق نص العنصر النائب داخل حقول الإدخال ومناطق النص. يعمل نص العنصر النائب كتلميح للمستخدم حول ما يجب إدخاله، لذا يجب أن يكون مميزا بصريا عن نص الإدخال الفعلي -- عادة بلون أفتح وأحيانا بخط مائل.
تنسيق العنصر النائب
/* تنسيق أساسي للعنصر النائب */
.form-input::placeholder {
color: #9ca3af;
opacity: 1; /* مهم لـ Firefox */
font-style: italic;
}
/* تنسيق عنصر نائب مختلف لحقول البحث */
.search-input::placeholder {
color: #6b7280;
font-style: normal;
font-weight: 500;
}
/* عنصر نائب يختفي عند التركيز */
.form-input:focus::placeholder {
opacity: 0;
transition: opacity 0.2s ease;
}
/* عنصر نائب متحرك ينزلق لأعلى عند التركيز */
.floating-label-group {
position: relative;
}
.floating-label-group .form-input::placeholder {
color: transparent;
}
.floating-label-group .label {
position: absolute;
left: 14px;
top: 50%;
transform: translateY(-50%);
color: #9ca3af;
font-size: 1rem;
pointer-events: none;
transition: all 0.2s ease;
}
.floating-label-group .form-input:focus ~ .label,
.floating-label-group .form-input:not(:placeholder-shown) ~ .label {
top: -8px;
left: 10px;
font-size: 0.75rem;
color: #3b82f6;
background-color: #fff;
padding: 0 4px;
}
:placeholder-shown تتطابق مع عنصر إدخال عندما يكون العنصر النائب مرئيا (أي أن حقل الإدخال فارغ). مع محدد الأخ المجاور ~، هذا هو المفتاح لإنشاء تسميات عائمة بـ CSS فقط دون أي JavaScript. عندما يكتب المستخدم شيئا، يتم تفعيل :not(:placeholder-shown) ويمكنك تحريك التسمية لأعلى.تنسيق مناطق النص
تشترك مناطق النص في معظم خصائص التنسيق مع حقول النص، لكن لها خاصية فريدة مهمة: resize. بشكل افتراضي، يمكن للمستخدم تغيير حجم مناطق النص (تظهر مقبض سحب في الزاوية اليمنى السفلية). يمكنك التحكم في هذا السلوك باستخدام خاصية resize.
تنسيق منطقة النص والتحكم في تغيير الحجم
/* أنماط منطقة النص الأساسية */
.form-textarea {
display: block;
width: 100%;
min-height: 120px;
padding: 10px 14px;
font-family: inherit;
font-size: 1rem;
line-height: 1.6;
color: #1a1a2e;
background-color: #fff;
border: 2px solid #d1d5db;
border-radius: 8px;
outline: none;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
box-sizing: border-box;
}
/* خيارات تغيير الحجم */
.form-textarea--vertical {
resize: vertical; /* تغيير الحجم عموديا فقط (الأكثر شيوعا) */
}
.form-textarea--horizontal {
resize: horizontal; /* تغيير الحجم أفقيا فقط (نادر) */
}
.form-textarea--both {
resize: both; /* تغيير الحجم في كلا الاتجاهين (افتراضي) */
}
.form-textarea--none {
resize: none; /* منع تغيير الحجم بالكامل */
}
/* تأثير منطقة نص متنامية تلقائيا (يتطلب حد أدنى وأقصى للارتفاع) */
.form-textarea--auto {
resize: none;
min-height: 80px;
max-height: 300px;
overflow-y: auto;
}
/* حالة التركيز */
.form-textarea:focus {
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.25);
}
resize: vertical (وهو الخيار الأكثر شيوعا)، عيّن أيضا min-height حتى لا يتمكن المستخدم من تقليص منطقة النص إلى حجم غير قابل للاستخدام، واختياريا max-height لمنعها من النمو خارج المساحة المتاحة.القوائم المنسدلة المخصصة
عناصر القوائم المنسدلة صعبة التنسيق بشكل ملحوظ لأن الجزء المنسدل (قائمة الخيارات) يُعرض بواسطة نظام التشغيل وهو محصن إلى حد كبير من CSS. ومع ذلك، يمكنك تنسيق مشغل القائمة المنسدلة (الحالة المغلقة التي تعرض القيمة المحددة) عن طريق إعادة تعيين مظهره وإضافة أنماط مخصصة وسهم منسدل مخصص.
تنسيق عنصر القائمة المنسدلة
/* تنسيق القائمة المنسدلة المخصصة */
.form-select {
display: block;
width: 100%;
padding: 10px 40px 10px 14px;
font-family: inherit;
font-size: 1rem;
line-height: 1.5;
color: #1a1a2e;
background-color: #fff;
border: 2px solid #d1d5db;
border-radius: 8px;
outline: none;
appearance: none;
-webkit-appearance: none;
cursor: pointer;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
/* سهم منسدل مخصص باستخدام background-image */
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%236b7280' d='M1.41 0L6 4.58 10.59 0 12 1.41l-6 6-6-6z'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 14px center;
background-size: 12px 8px;
}
.form-select:hover {
border-color: #9ca3af;
}
.form-select:focus {
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.25);
}
/* القائمة المنسدلة المعطلة */
.form-select:disabled {
background-color: #f3f4f6;
color: #9ca3af;
cursor: not-allowed;
}
/* القائمة المنسدلة المتعددة (تظهر كقائمة صندوق) */
.form-select[multiple] {
padding: 8px;
height: auto;
background-image: none;
}
.form-select[multiple] option {
padding: 6px 10px;
border-radius: 4px;
}
.form-select[multiple] option:checked {
background-color: #3b82f6;
color: white;
}
background-image. يتم ترميز SVG بتنسيق URL بحيث يمكن تضمينه مباشرة في CSS دون ملف منفصل. هذه التقنية تمنحك تحكما كاملا في لون السهم وحجمه وشكله مع الحفاظ على كل شيء في ملف CSS واحد. يمكنك أيضا استخدام حرف Unicode مثل الشيفرون أو مثلث CSS مصنوع بالحدود، لكن نهج SVG يوفر أكبر قدر من المرونة.مربعات الاختيار وأزرار الراديو المخصصة
مربعات الاختيار وأزرار الراديو هي ربما أصعب عناصر النماذج في التنسيق لأن عرضها الأصلي مدمج بعمق في نظام التشغيل. النهج الحديث هو إخفاء عنصر التحكم الأصلي باستخدام appearance: none ثم بناء التمثيل المرئي بالكامل باستخدام CSS، بما في ذلك حالة التحديد وحلقة التركيز وأي رسوم متحركة.
مربع اختيار مخصص
/* مربع اختيار مخصص */
.custom-checkbox {
appearance: none;
-webkit-appearance: none;
width: 22px;
height: 22px;
border: 2px solid #d1d5db;
border-radius: 5px;
background-color: #fff;
cursor: pointer;
position: relative;
transition: all 0.15s ease;
flex-shrink: 0;
vertical-align: middle;
}
.custom-checkbox:hover {
border-color: #3b82f6;
background-color: #eff6ff;
}
.custom-checkbox:focus-visible {
outline: none;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.4);
}
/* حالة التحديد */
.custom-checkbox:checked {
background-color: #3b82f6;
border-color: #3b82f6;
}
/* علامة الاختيار باستخدام عنصر زائف */
.custom-checkbox:checked::after {
content: "";
position: absolute;
left: 6px;
top: 2px;
width: 6px;
height: 12px;
border: solid white;
border-width: 0 2.5px 2.5px 0;
transform: rotate(45deg);
}
/* حالة غير محددة (محدد جزئيا) */
.custom-checkbox:indeterminate {
background-color: #3b82f6;
border-color: #3b82f6;
}
.custom-checkbox:indeterminate::after {
content: "";
position: absolute;
left: 4px;
top: 8px;
width: 10px;
height: 2.5px;
background-color: white;
border-radius: 1px;
}
زر راديو مخصص
/* زر راديو مخصص */
.custom-radio {
appearance: none;
-webkit-appearance: none;
width: 22px;
height: 22px;
border: 2px solid #d1d5db;
border-radius: 50%;
background-color: #fff;
cursor: pointer;
position: relative;
transition: all 0.15s ease;
flex-shrink: 0;
vertical-align: middle;
}
.custom-radio:hover {
border-color: #3b82f6;
background-color: #eff6ff;
}
.custom-radio:focus-visible {
outline: none;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.4);
}
/* حالة التحديد -- دائرة داخلية ممتلئة */
.custom-radio:checked {
border-color: #3b82f6;
background-color: #fff;
}
.custom-radio:checked::after {
content: "";
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 10px;
height: 10px;
border-radius: 50%;
background-color: #3b82f6;
}
/* تسميات مربعات الاختيار والراديو */
.form-check {
display: flex;
align-items: center;
gap: 10px;
cursor: pointer;
padding: 6px 0;
font-size: 0.95rem;
color: #374151;
}
.form-check:hover .custom-checkbox,
.form-check:hover .custom-radio {
border-color: #3b82f6;
}
مفاتيح التبديل بـ CSS
مفاتيح التبديل هي نمط واجهة مستخدم شائع لإعدادات التشغيل/الإيقاف الثنائية. ليست عنصر تحكم نموذج HTML أصلي، لكن يمكنك بناؤها باستخدام مربع اختيار منسق. الحيلة هي تحويل مربع الاختيار بصريا إلى مسار مفتاح مع مقبض منزلق باستخدام CSS وحده، مع الحفاظ على مربع الاختيار الأساسي لإمكانية الوصول وتقديم النموذج.
مفتاح تبديل بـ CSS فقط
/* مفتاح تبديل مبني من مربع اختيار */
.toggle-switch {
appearance: none;
-webkit-appearance: none;
width: 48px;
height: 26px;
background-color: #d1d5db;
border: none;
border-radius: 13px;
cursor: pointer;
position: relative;
transition: background-color 0.25s ease;
flex-shrink: 0;
vertical-align: middle;
}
/* المقبض المنزلق */
.toggle-switch::after {
content: "";
position: absolute;
top: 3px;
left: 3px;
width: 20px;
height: 20px;
background-color: white;
border-radius: 50%;
transition: transform 0.25s ease;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
}
/* حالة التحديد (مفعّل) */
.toggle-switch:checked {
background-color: #3b82f6;
}
.toggle-switch:checked::after {
transform: translateX(22px);
}
/* حلقة التركيز */
.toggle-switch:focus-visible {
outline: none;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.4);
}
/* الحالة المعطلة */
.toggle-switch:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* مفتاح تبديل مع تسمية */
.toggle-label {
display: flex;
align-items: center;
gap: 12px;
cursor: pointer;
font-size: 0.95rem;
}
.toggle-label span {
user-select: none;
}
تنسيق مدخلات الملفات
مدخلات الملفات (<input type="file">) هي تاريخيا أكثر عناصر التحكم عنادا في التنسيق. كان النهج التقليدي هو إخفاء مدخل الملف وتشغيله من تسمية منسقة. ومع ذلك، يدعم CSS الحديث الآن العنصر الزائف ::file-selector-button، الذي يتيح لك تنسيق زر "اختيار ملف" مباشرة.
تنسيق مدخلات الملفات
/* النهج الحديث: تنسيق زر اختيار الملف */
.form-file {
display: block;
width: 100%;
padding: 8px;
font-family: inherit;
font-size: 0.95rem;
color: #374151;
background-color: #fff;
border: 2px dashed #d1d5db;
border-radius: 8px;
cursor: pointer;
transition: border-color 0.2s ease;
}
.form-file:hover {
border-color: #3b82f6;
}
.form-file::file-selector-button {
padding: 8px 16px;
margin-right: 12px;
font-family: inherit;
font-size: 0.875rem;
font-weight: 600;
color: white;
background-color: #3b82f6;
border: none;
border-radius: 6px;
cursor: pointer;
transition: background-color 0.2s ease;
}
.form-file::file-selector-button:hover {
background-color: #2563eb;
}
/* بديل: مدخل ملف مخصص باستخدام تسمية */
.file-input-custom {
position: relative;
display: inline-block;
}
.file-input-custom input[type="file"] {
position: absolute;
width: 0;
height: 0;
opacity: 0;
overflow: hidden;
}
.file-input-custom .file-label {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 10px 20px;
font-size: 0.95rem;
font-weight: 600;
color: #3b82f6;
background-color: #eff6ff;
border: 2px solid #3b82f6;
border-radius: 8px;
cursor: pointer;
transition: all 0.2s ease;
}
.file-input-custom .file-label:hover {
background-color: #3b82f6;
color: white;
}
تنسيق مدخلات النطاق
مدخلات النطاق (<input type="range">) تنشئ عناصر تحكم منزلقة. تتكون من مسار (الشريط الأفقي) وإبهام (المقبض القابل للسحب). لتنسيقها، تحتاج لاستهداف عناصر زائفة خاصة بالمتصفح للمسار والإبهام، لأنه لا يوجد عنصر زائف موحد قياسي بعد. العناصر الزائفة الرئيسية هي ::-webkit-slider-runnable-track و::-webkit-slider-thumb لـ Chrome وSafari، و::-moz-range-track و::-moz-range-thumb لـ Firefox.
منزلق نطاق مخصص
/* إعادة تعيين مدخل النطاق الأساسي */
.form-range {
appearance: none;
-webkit-appearance: none;
width: 100%;
height: 8px;
background: transparent;
cursor: pointer;
outline: none;
}
/* المسار (Chrome، Safari، Edge) */
.form-range::-webkit-slider-runnable-track {
height: 8px;
background: #e5e7eb;
border-radius: 4px;
}
/* المسار (Firefox) */
.form-range::-moz-range-track {
height: 8px;
background: #e5e7eb;
border-radius: 4px;
border: none;
}
/* الإبهام (Chrome، Safari، Edge) */
.form-range::-webkit-slider-thumb {
appearance: none;
-webkit-appearance: none;
width: 22px;
height: 22px;
background: #3b82f6;
border-radius: 50%;
border: 3px solid white;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
margin-top: -7px; /* توسيط الإبهام على المسار */
transition: transform 0.15s ease, box-shadow 0.15s ease;
}
/* الإبهام (Firefox) */
.form-range::-moz-range-thumb {
width: 22px;
height: 22px;
background: #3b82f6;
border-radius: 50%;
border: 3px solid white;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
transition: transform 0.15s ease, box-shadow 0.15s ease;
}
/* تأثير التمرير على الإبهام */
.form-range::-webkit-slider-thumb:hover {
transform: scale(1.15);
box-shadow: 0 2px 8px rgba(59, 130, 246, 0.4);
}
.form-range::-moz-range-thumb:hover {
transform: scale(1.15);
box-shadow: 0 2px 8px rgba(59, 130, 246, 0.4);
}
/* حلقة التركيز */
.form-range:focus-visible::-webkit-slider-thumb {
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.4);
}
.form-range:focus-visible::-moz-range-thumb {
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.4);
}
/* الجزء الممتلئ من المسار (Firefox فقط يدعم هذا أصليا) */
.form-range::-moz-range-progress {
height: 8px;
background-color: #3b82f6;
border-radius: 4px;
}
::-moz-range-progress. لـ Chrome وSafari، تحتاج لاستخدام تدرج CSS على المسار وتحديثه ديناميكيا بـ JavaScript، أو استخدام نهج متغيرات CSS المخصصة. هذا أحد التناقضات المتبقية في تنسيق النماذج عبر المتصفحات.الفئات الزائفة للتحقق من النماذج
يوفر CSS مجموعة غنية من الفئات الزائفة لتنسيق عناصر النماذج بناء على حالة التحقق الخاصة بها. تستجيب هذه الفئات الزائفة لسمات التحقق في HTML5 مثل required وpattern وmin وmax وminlength وmaxlength وtype. باستخدام هذه الفئات الزائفة، يمكنك تقديم ملاحظات مرئية فورية للمستخدمين دون أي JavaScript.
الفئات الزائفة للتحقق
/* :valid -- قيمة الإدخال تلبي جميع قيود التحقق */
.form-input:valid {
border-color: #10b981;
}
/* :invalid -- قيمة الإدخال لا تلبي قيود التحقق */
.form-input:invalid {
border-color: #ef4444;
}
/* :required -- الإدخال يحتوي على سمة required */
.form-input:required {
border-left: 3px solid #f59e0b;
}
/* :optional -- الإدخال لا يحتوي على سمة required */
.form-input:optional {
border-left: 3px solid #d1d5db;
}
/* :in-range -- قيمة إدخال الرقم/التاريخ ضمن نطاق min/max */
.form-input:in-range {
border-color: #10b981;
}
/* :out-of-range -- قيمة إدخال الرقم/التاريخ خارج نطاق min/max */
.form-input:out-of-range {
border-color: #ef4444;
background-color: #fef2f2;
}
/* :placeholder-shown -- العنصر النائب مرئي حاليا (الإدخال فارغ) */
.form-input:placeholder-shown {
border-color: #d1d5db; /* حدود محايدة عندما يكون فارغا */
}
/* دمج لإظهار ألوان التحقق فقط عندما يكتب المستخدم شيئا */
.form-input:not(:placeholder-shown):valid {
border-color: #10b981;
}
.form-input:not(:placeholder-shown):invalid {
border-color: #ef4444;
}
نمط دمج :not(:placeholder-shown) مع :valid و:invalid مهم بشكل خاص. بدونه، سيظهر حقل إدخال مطلوب فارغ بالتنسيق غير الصالح (الأحمر) بمجرد تحميل الصفحة، قبل أن يتفاعل المستخدم مع النموذج. بالتحقق من أن العنصر النائب غير ظاهر (مما يعني أن المستخدم كتب شيئا)، تعرض ملاحظات التحقق فقط بعد محاولة إدخال المستخدم.
ملاحظات مرئية بالأيقونات
/* أيقونات التحقق باستخدام عناصر زائفة على غلاف */
.input-group {
position: relative;
}
.input-group .form-input {
padding-right: 40px;
}
/* أيقونة النجاح */
.input-group .form-input:not(:placeholder-shown):valid ~ .validation-icon::after {
content: "\2713"; /* علامة صح */
position: absolute;
right: 12px;
top: 50%;
transform: translateY(-50%);
color: #10b981;
font-size: 1.25rem;
font-weight: bold;
}
/* أيقونة الخطأ */
.input-group .form-input:not(:placeholder-shown):invalid ~ .validation-icon::after {
content: "\2717"; /* علامة خطأ */
position: absolute;
right: 12px;
top: 50%;
transform: translateY(-50%);
color: #ef4444;
font-size: 1.25rem;
font-weight: bold;
}
/* رسالة خطأ تظهر أسفل المدخلات غير الصالحة */
.error-message {
display: none;
font-size: 0.8rem;
color: #ef4444;
margin-top: 4px;
padding-left: 2px;
}
.form-input:not(:placeholder-shown):invalid ~ .error-message {
display: block;
}
الحالات المعطلة والقراءة فقط
غالبا ما تحتوي النماذج على مدخلات غير قابلة للتحرير حاليا، إما لأنها معطلة (لا يمكن للمستخدم التفاعل معها على الإطلاق) أو للقراءة فقط (يمكن للمستخدم رؤية النص وتحديده لكن لا يمكنه تعديله). يوفر CSS فئات زائفة لكلتا الحالتين، ومن المهم تنسيقها بوضوح حتى يفهم المستخدمون أي الحقول يمكنهم تحريرها وأيها لا.
أنماط المعطل والقراءة فقط
/* الحالة المعطلة -- رمادية، بدون تفاعل */
.form-input:disabled,
.form-select:disabled,
.form-textarea:disabled {
background-color: #f3f4f6;
color: #9ca3af;
border-color: #e5e7eb;
cursor: not-allowed;
opacity: 0.7;
}
/* حالة القراءة فقط -- مرئية لكن غير قابلة للتحرير */
.form-input:read-only {
background-color: #f9fafb;
color: #4b5563;
border-color: #e5e7eb;
cursor: default;
}
/* إزالة حلقة التركيز على القراءة فقط لأن المستخدم لا يمكنه التحرير */
.form-input:read-only:focus {
border-color: #e5e7eb;
box-shadow: none;
}
/* مربع الاختيار والراديو المعطل */
.custom-checkbox:disabled,
.custom-radio:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.custom-checkbox:disabled + label,
.custom-radio:disabled + label {
color: #9ca3af;
cursor: not-allowed;
}
تنسيق النماذج المتاح للجميع
تنسيق النماذج المتاح للجميع يتجاوز المظهر المرئي فقط. يتضمن ضمان أن جميع المستخدمين، بما في ذلك الذين يستخدمون قارئات الشاشة والتنقل بلوحة المفاتيح والتقنيات المساعدة الأخرى، يمكنهم استخدام نماذجك بفعالية. إليك المبادئ الأساسية لتنسيق النماذج المتاح وتقنيات CSS التي تدعمها.
أفضل ممارسات إمكانية الوصول في CSS
/* 1. ضمان تباين لوني كافٍ لجميع الحالات */
.form-input {
color: #1a1a2e; /* نص داكن على خلفية فاتحة: نسبة 15:1 */
border-color: #6b7280; /* نسبة 4.6:1 مقابل الأبيض */
}
.form-input::placeholder {
color: #6b7280; /* نسبة تباين 4.5:1 على الأقل */
}
/* 2. لا تعتمد أبدا على اللون وحده للتحقق -- أضف أيقونات أو حدود أو نص */
.form-input:not(:placeholder-shown):invalid {
border-color: #ef4444;
border-width: 2px;
background-image: url("data:image/svg+xml,..."); /* أيقونة خطأ */
background-repeat: no-repeat;
background-position: right 12px center;
padding-right: 40px;
}
/* 3. احترم تفضيلات الحركة المخفضة */
@media (prefers-reduced-motion: reduce) {
.form-input,
.custom-checkbox,
.custom-radio,
.toggle-switch,
.toggle-switch::after {
transition: none;
}
}
/* 4. دعم وضع التباين العالي */
@media (forced-colors: active) {
.custom-checkbox:checked,
.custom-radio:checked {
forced-color-adjust: none;
background-color: Highlight;
border-color: Highlight;
}
.form-input:focus {
outline: 2px solid Highlight;
}
}
/* 5. أهداف لمس كبيرة للهاتف المحمول (44x44px على الأقل) */
@media (pointer: coarse) {
.custom-checkbox,
.custom-radio {
min-width: 44px;
min-height: 44px;
}
.form-check {
min-height: 44px;
padding: 10px 0;
}
.toggle-switch {
min-width: 52px;
min-height: 44px;
}
}
/* 6. تسميات مرئية -- لا تخفها أبدا، لكن يمكنك إخفاؤها بصريا إذا لزم الأمر */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.sr-only فقط عندما تكون التسمية المرئية مستحيلة حقا (مثل حقل بحث بأيقونة عدسة مكبرة مرئية). في جميع الحالات تقريبا، يجب أن يكون لحقول الإدخال عنصر <label> مرئي مرتبط عبر سمة for. العناصر النائبة ليست بديلا عن التسميات -- فهي تختفي عندما يبدأ المستخدم بالكتابة، ولا تترك أي سياق لما هو الحقل.مثال نموذج منسق كامل
دعنا نجمع كل شيء معا في نموذج منسق كامل وجاهز للإنتاج. هذا المثال يجمع جميع التقنيات التي غطيناها: إعادة تعيين الأنماط، حقول النص المخصصة، القوائم المنسدلة، مربعات الاختيار، أزرار الراديو، مفتاح التبديل، حالات التحقق، وميزات إمكانية الوصول.
نموذج اتصال كامل بتنسيق مخصص
<style>
.styled-form {
max-width: 560px;
margin: 0 auto;
padding: 32px;
background: #ffffff;
border-radius: 16px;
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08);
font-family: system-ui, -apple-system, sans-serif;
}
.styled-form h2 {
margin: 0 0 24px 0;
font-size: 1.5rem;
color: #1a1a2e;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 6px;
font-size: 0.875rem;
font-weight: 600;
color: #374151;
}
.form-group label .required {
color: #ef4444;
margin-left: 2px;
}
.form-group .hint {
display: block;
font-size: 0.8rem;
color: #6b7280;
margin-top: 4px;
}
/* المدخلات ومنطقة النص */
.styled-form .input,
.styled-form select,
.styled-form textarea {
display: block;
width: 100%;
padding: 10px 14px;
font-family: inherit;
font-size: 1rem;
line-height: 1.5;
color: #1a1a2e;
background-color: #fff;
border: 2px solid #d1d5db;
border-radius: 8px;
outline: none;
appearance: none;
-webkit-appearance: none;
box-sizing: border-box;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.styled-form .input:focus,
.styled-form select:focus,
.styled-form textarea:focus {
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2);
}
.styled-form .input:not(:placeholder-shown):valid {
border-color: #10b981;
}
.styled-form .input:not(:placeholder-shown):invalid {
border-color: #ef4444;
}
.styled-form textarea {
resize: vertical;
min-height: 120px;
}
.styled-form select {
padding-right: 40px;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8'%3E%3Cpath fill='%236b7280' d='M1.41 0L6 4.58 10.59 0 12 1.41l-6 6-6-6z'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 14px center;
cursor: pointer;
}
/* مجموعات مربعات الاختيار والراديو */
.check-group {
display: flex;
flex-direction: column;
gap: 8px;
}
.check-item {
display: flex;
align-items: center;
gap: 10px;
cursor: pointer;
}
/* زر الإرسال */
.styled-form .btn-submit {
display: block;
width: 100%;
padding: 12px 24px;
margin-top: 28px;
font-family: inherit;
font-size: 1rem;
font-weight: 700;
color: white;
background-color: #3b82f6;
border: none;
border-radius: 8px;
cursor: pointer;
transition: background-color 0.2s ease, transform 0.1s ease;
}
.styled-form .btn-submit:hover {
background-color: #2563eb;
}
.styled-form .btn-submit:active {
transform: scale(0.98);
}
.styled-form .btn-submit:focus-visible {
outline: 3px solid #3b82f6;
outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
.styled-form * {
transition: none !important;
}
}
</style>
<form class="styled-form" novalidate>
<h2>تواصل معنا</h2>
<div class="form-group">
<label for="name">الاسم الكامل <span class="required">*</span></label>
<input type="text" id="name" class="input" placeholder="محمد أحمد"
required minlength="2">
</div>
<div class="form-group">
<label for="email">البريد الإلكتروني <span class="required">*</span></label>
<input type="email" id="email" class="input" placeholder="mohammed@example.com"
required>
</div>
<div class="form-group">
<label for="subject">الموضوع</label>
<select id="subject">
<option value="">اختر موضوعا...</option>
<option value="general">استفسار عام</option>
<option value="support">الدعم الفني</option>
<option value="feedback">ملاحظات</option>
</select>
</div>
<div class="form-group">
<label for="message">الرسالة <span class="required">*</span></label>
<textarea id="message" placeholder="اكتب رسالتك..." required></textarea>
</div>
<div class="form-group">
<label>طريقة التواصل المفضلة</label>
<div class="check-group">
<label class="check-item">
<input type="radio" name="contact" value="email" class="custom-radio" checked>
البريد الإلكتروني
</label>
<label class="check-item">
<input type="radio" name="contact" value="phone" class="custom-radio">
الهاتف
</label>
</div>
</div>
<div class="form-group">
<label class="check-item">
<input type="checkbox" class="custom-checkbox" required>
أوافق على الشروط والأحكام <span class="required">*</span>
</label>
</div>
<div class="form-group">
<label class="toggle-label">
<input type="checkbox" class="toggle-switch">
<span>الاشتراك في النشرة الإخبارية</span>
</label>
</div>
<button type="submit" class="btn-submit">إرسال الرسالة</button>
</form>
التمرين 1: نموذج تسجيل مخصص
أنشئ نموذج تسجيل مستخدم كامل يحتوي على الحقول التالية: اسم المستخدم (حقل نص بحد أدنى 3 أحرف)، البريد الإلكتروني (حقل بريد إلكتروني)، كلمة المرور (حقل كلمة مرور بحد أدنى 8 أحرف)، تأكيد كلمة المرور، تاريخ الميلاد (حقل تاريخ)، الدولة (قائمة منسدلة منسقة مخصصة بـ 5 دول على الأقل)، الجنس (أزرار راديو منسقة مخصصة)، الاهتمامات (مربعات اختيار منسقة مخصصة بـ 4 خيارات على الأقل)، نبذة شخصية (منطقة نص بتغيير حجم عمودي فقط)، صورة الملف الشخصي (مدخل ملف منسق مخصص)، تفضيل الإشعارات (مفتاح تبديل)، ومنزلق نطاق العمر (مدخل نطاق من 18 إلى 100). نسّق جميع المدخلات بشكل متسق باستخدام التقنيات من هذا الدرس. أضف حالات التمرير والتركيز و focus-visible لكل عنصر تفاعلي. نفّذ ملاحظات تحقق بـ CSS فقط باستخدام الفئات الزائفة :valid و:invalid و:not(:placeholder-shown) و:required. أظهر أيقونات تحقق (علامة صح للصالح، X لغير الصالح) بجانب المدخلات. أضف تسمية عائمة على حقل البريد الإلكتروني. تأكد من أن جميع عناصر التحكم المخصصة (مربعات الاختيار، الراديو، التبديل، النطاق) قابلة للوصول بالكامل بلوحة المفاتيح مع مؤشرات تركيز مرئية. أضف دعم prefers-reduced-motion لتعطيل جميع الانتقالات للمستخدمين الذين يفضلون حركة مخفضة.
التمرين 2: نموذج متعدد الخطوات مع شريط تقدم
ابنِ واجهة نموذج متعدد الخطوات حيث كل خطوة هي مجموعة حقول يتم إظهارها أو إخفاؤها (يمكنك محاكاة ذلك بفئات CSS). الخطوة 1 تجمع المعلومات الشخصية (الاسم، البريد الإلكتروني، الهاتف)، الخطوة 2 تجمع التفضيلات (اللون المفضل عبر أزرار الراديو، الهوايات عبر مربعات الاختيار، نطاق الميزانية عبر منزلق النطاق)، والخطوة 3 هي خطوة مراجعة مع مربع اختيار الشروط ومفتاح تبديل لرسائل التسويق. صمم شريط تقدم في الأعلى يشير بصريا للخطوة الحالية باستخدام CSS (مثل ثلاث دوائر متصلة بخط، مع تمييز الخطوات المكتملة). نسّق كل مجموعة حقول بتباعد وتجميع متسق. اجعل النموذج متجاوبا بالكامل: على الشاشات الصغيرة، يجب أن تتراكم التسميات فوق المدخلات، وعلى الشاشات الأعرض، يمكن أن تجلس التسميات بجانب مدخلاتها. استخدم الفئة الزائفة :disabled لتنسيق زر "السابق" كمعطل في الخطوة 1 وزر "التالي" كمعطل في الخطوة 3. نسّق زر "إرسال" النهائي بشكل مميز عن أزرار التنقل. أضف تنسيق تحقق لكل حقل مطلوب وتأكد من أن النموذج يلبي معايير إمكانية الوصول مع ارتباطات التسمية الصحيحة وإدارة التركيز وتباين الألوان.