استایل‌دهی به فرم‌ها؛ جایی که CSS با محدودیت‌های مرورگر دست‌وپنجه نرم می‌کنه

تا اینجا با المنت‌های معمولی مثل div، span، تصویر و متن کار کردیم. اما فرم‌ها یه دنیای جدا هستن. المنت‌های فرم مثل input، button و select، رفتار پیش‌فرض خیلی خاصی از خود مرورگر و سیستم‌عامل به ارث می‌برن که استایل‌دهی بهشون رو کمی متفاوت از بقیه‌ی المنت‌ها می‌کنه. تو این درس می‌خوایم ببینیم چطور می‌شه این المنت‌ها رو کنترل کرد.

چرا فرم‌ها این‌قدر سرکش هستن؟

یه input یا button، برخلاف یه div، تو هر مرورگر و هر سیستم‌عاملی ظاهر پیش‌فرض متفاوتی داره؛ چون این المنت‌ها معمولاً از استایل‌های native خود سیستم‌عامل (مثل ظاهر دکمه‌های ویندوز یا مک) استفاده می‌کنن. همین باعث می‌شه یه فرم بدون استایل، تو مرورگرهای مختلف ظاهر کاملاً متفاوتی داشته باشه.

ریست کردن استایل پایه‌ی input و button

اولین قدم معمولاً حذف استایل‌های عجیب پیش‌فرض هست:

input,
button,
select,
textarea {
  font: inherit;
  color: inherit;
  border: 1px solid #ccc;
  border-radius: 4px;
  padding: 8px 12px;
}

نکته‌ی font: inherit خیلی مهمه؛ چون به‌طور پیش‌فرض، خیلی از این المنت‌ها فونت خودشون رو از سیستم‌عامل می‌گیرن، نه از فونتی که شما تو body تعریف کردید. بدون این خط، ممکنه فونت دکمه‌ها و اینپوت‌ها با بقیه‌ی سایت هماهنگ نباشه.

appearance: none برای حذف استایل native

بعضی وقت‌ها فقط تنظیم border و padding کافی نیست؛ خود ظاهر پایه‌ی المنت (مثل فلش کشویی select یا دایره‌ی رادیو باتن) از سیستم‌عامل میاد و با CSS معمولی قابل تغییر نیست. برای این موارد از appearance: none استفاده می‌کنیم:

select {
  appearance: none;
  background-image: url("data:image/svg+xml,...");
  background-repeat: no-repeat;
  background-position: right 12px center;
}

با appearance: none، فلش پیش‌فرض select حذف می‌شه، و بعد خودتون می‌تونید یه فلش دلخواه (مثلاً با یه background-image) جایگزینش کنید.

استایل‌دهی به checkbox و radio

این دو المنت از همه سرکش‌ترن، چون ظاهرشون کاملاً وابسته به سیستم‌عامله. با appearance: none می‌تونید ظاهر پیش‌فرض رو کاملاً حذف کنید و از صفر بسازیدشون:

input[type="checkbox"] {
  appearance: none;
  width: 20px;
  height: 20px;
  border: 2px solid #999;
  border-radius: 4px;
  display: inline-grid;
  place-content: center;
}

input[type="checkbox"]:checked {
  background: #3b82f6;
  border-color: #3b82f6;
}

input[type="checkbox"]:checked::before {
  content: "✓";
  color: white;
  font-size: 14px;
}

نکته‌ی جالب اینجا استفاده از display: inline-grid و place-content: center هست؛ این ترکیب یه راه ساده برای وسط‌چین کردن علامت تیک داخل خود checkbox هست، بدون نیاز به absolute positioning.

استایل‌دهی به دکمه‌ها

button معمولاً کمتر از input سرکشیه، ولی چند نکته‌ی ریز داره. یه مشکل رایج اینه که button تو بعضی مرورگرها یه border و background پیش‌فرض عجیب داره:

button {
  appearance: none;
  border: none;
  cursor: pointer;
  background: #3b82f6;
  color: white;
  padding: 10px 20px;
  border-radius: 6px;
}

button:hover {
  background: #2563eb;
}

button:disabled {
  cursor: not-allowed;
  opacity: 0.6;
}

فراموش نکنید cursor: pointer بذارید؛ برخلاف لینک‌ها (a) که به‌طور پیش‌فرض cursor مناسب دارن، دکمه‌ها تو بعضی مرورگرها cursor پیش‌فرضشون default (فلش معمولی) هست، نه دست اشاره‌گر.

Pseudo-class های مخصوص فرم

فرم‌ها یه سری pseudo-class مخصوص به خودشون دارن که برای بقیه‌ی المنت‌ها معنایی ندارن:

Pseudo-classکاربرد
:focusوقتی المنت فوکوس داره (مثلاً با کلیک یا Tab)
:disabledوقتی المنت غیرفعاله
:checkedوقتی checkbox یا radio تیک خورده
:invalidوقتی مقدار وارد‌شده با validation مطابقت نداره
:placeholder-shownوقتی input خالیه و placeholder نمایش داده می‌شه
input:invalid {
  border-color: #dc2626;
}

input:focus {
  outline: 2px solid #3b82f6;
  outline-offset: 2px;
}
نکته مهم: هیچ‌وقت outline: none رو بدون جایگزین مناسب روی input یا button نذارید. این outline برای کاربرهایی که با کیبورد navigate می‌کنن (نه ماوس) حیاتیه؛ بدون اون، کاربر نمی‌فهمه الان روی کدوم المنت فوکوس داره. اگه ظاهر outline پیش‌فرض رو دوست ندارید، به‌جای حذفش، یه استایل جایگزین (مثل box-shadow یا border رنگی) براش تعریف کنید.

:focus-visible: فوکوس فقط برای کیبورد

یه نکته‌ی ظریف‌تر: بعضی وقت‌ها می‌خواید استایل فوکوس فقط وقتی نمایش داده بشه که کاربر با کیبورد navigate می‌کنه، نه وقتی با ماوس کلیک می‌کنه (چون خیلی از طراح‌ها فکر می‌کنن outline موقع کلیک ماوس بصری «شلوغ» به نظر می‌رسه). برای این حالت از :focus-visible به‌جای :focus استفاده کنید:

button:focus-visible {
  outline: 2px solid #3b82f6;
}

مرورگر خودش تشخیص می‌ده که آیا فوکوس از کیبورد اومده یا کلیک ماوس، و :focus-visible فقط تو حالت اول فعال می‌شه.

اشتباه رایج: استفاده از appearance: none بدون جایگزین بصری

یه اشتباه رایج اینه که کسی appearance: none رو می‌ذاره تا ظاهر پیش‌فرض یه checkbox یا select رو حذف کنه، ولی فراموش می‌کنه هیچ استایل جایگزینی (مثل border یا background) براش تعریف کنه. نتیجه این می‌شه که المنت عملاً نامرئی یا کاملاً بی‌شکل به نظر می‌رسه، در حالی که هنوز از نظر عملکردی سرجاشه.

جمع‌بندی

استایل‌دهی به فرم‌ها نیاز به توجه بیشتری نسبت به بقیه‌ی المنت‌ها داره، چون این المنت‌ها استایل native خودشون رو از سیستم‌عامل به ارث می‌برن. appearance: none کلید اصلی برای حذف این استایل پیش‌فرض و ساختن ظاهر دلخواهه، ولی همیشه باید جایگزین مناسب براش بذارید. pseudo-class هایی مثل :focus-visible، :checked و :invalid هم ابزارهای اصلی برای واکنش‌نشون‌دادن به حالت‌های مختلف فرم هستن. و هیچ‌وقت دسترس‌پذیری فوکوس رو فدای زیبایی ظاهری نکنید.