تاروپود — خودآموز HTML و CSS

فصل ۶ از ۱۱

پیشرفت ترم
۰٪

ترم ۶ · حرفه‌ای و واقعی

دسترس‌پذیری عمیق

فصل ۶پیش‌نمایش رایگان
۱۱ دقیقه مطالعه فصل ۶

در این فصل چه می‌سازی#

در ترم ۱ یاد گرفتی که HTMLِ semantic نصفِ کارِ دسترس‌پذیری را خودش انجام می‌دهد. حالا نصفِ دیگر را می‌سازیم — همان جاهایی که با CSS و انتخاب‌های خودت یا صفحه را برای همه باز می‌کنی، یا بی‌آنکه بفهمی در را می‌بندی. آخرِ فصل یک کارتِ پروفایلِ کوچک داری که کاملاً با صفحه‌کلید کار می‌کند، هر چیزِ قابل‌فوکوس یک حلقهٔ focusِ دیدنی دارد، متنش خوانا است، و ARIA را فقط جایی گذاشته‌ای که واقعاً لازم بوده. مهم‌تر از خودِ کارت، یاد می‌گیری چطور خرابی‌های دسترس‌پذیری را — که با چشم دیده نمی‌شوند — خودت پیدا کنی.

یک کارتِ پروفایل که یک مسیرِ نقطه‌چینِ حرکتِ صفحه‌کلید رویش کشیده شده: از منوی بالا شروع می‌شود، به دکمهٔ اصلی می‌رسد و بعد به دکمهٔ آیکونی؛ دورِ عنصرِ فعال یک قابِ ضخیمِ قرمز هست که همان حلقهٔ focus را نشان می‌دهد

آخر این فصل می‌توانی:

  • یک صفحه را فقط با Tab و Enter بپیمایی و خرابی‌هایش را پیدا کنی
  • برای هر چیزِ قابل‌فوکوس یک حلقهٔ focusِ دیدنی نگه داری
  • بگویی ARIA کِی لازم است و کِی به‌جای کمک ضرر می‌زند
  • رنگِ متن را به‌قدرِ کافی خوانا (contrast) نگه داری

قبل از شروع#

  • از ترم ۱ فصل ۹: پیمایش با Tab، landmark ها، alt درست — پایهٔ همین فصل است.
  • از ترم ۵ فصل ۱: :focus و :focus-visible — حلقهٔ focus را با همین می‌سازیم.

مثل همیشه فقط یک مرورگر لازم داری، یا فقط همین آزمایشگاه.

۱. تست با صفحه‌کلید: Tab را بزن#

خیلی‌ها با ماوس یا لمس کار نمی‌کنند: کسی که نمی‌بیند، کسی که دستش می‌لرزد، کسی که فقط صفحه‌کلید دارد. همهٔ این‌ها با یک کلید در صفحه حرکت می‌کنند: Tab. هر بار که Tab را بزنی، فوکوس به عنصرِ قابلِ‌تعاملِ بعدی می‌پرد — لینک، دکمه، فیلد — و Enter آن را فعال می‌کند.

این ساده‌ترین و مهم‌ترین تستِ دسترس‌پذیری است: ماوس را کنار بگذار و فقط با Tab کلِ صفحه را طی کن. در آزمایشگاه همین را بکن. حواست به دو چیز باشد: آیا به همه‌چیز می‌رسی؟ و ترتیبِ پرش‌ها منطقی است؟

ترتیبِ این پرش‌ها را خودت جدا تعیین نمی‌کنی؛ از ترتیبِ HTML می‌آید. عنصرها به همان ترتیبی که در فایل نوشته شده‌اند فوکوس می‌گیرند. برای همین یک قانونِ ساده داریم: چیزها را در HTML به همان ترتیبی بنویس که می‌خواهی کاربر به آن‌ها برسد؛ آن‌وقت ترتیبِ صفحه‌کلید خودش درست می‌شود.

۲. focus باید دیده شود#

وقتی با Tab حرکت می‌کنی، باید ببینی الان کجایی. مرورگر برای همین دورِ عنصرِ فعال یک حلقه می‌کشد. این حلقه برای کاربرِ صفحه‌کلید مثلِ نشانگرِ ماوس است — بی‌آن، گم می‌شود.

در ترم ۵ با :focus-visible آشنا شدی؛ اینجا ازش یک حلقهٔ تمیز و پُررنگ می‌سازیم:

:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}

چک کن: حالا در آزمایشگاه Tab بزن — دورِ هر لینک و دکمه یک قابِ قرمزِ واضح ظاهر می‌شود که با هر پرش جابه‌جا می‌شود. همیشه می‌دانی کجایی.

⚠️ مواظب باش: رایج‌ترین اشتباهِ دسترس‌پذیری در کلِ وب یک خطِ کوتاه است: outline: none. خیلی‌ها چون حلقهٔ پیش‌فرض را زشت می‌دانند حذفش می‌کنند — و کاربرِ صفحه‌کلید را کاملاً کور می‌کنند. اگر حلقهٔ پیش‌فرض را دوست نداری، جایگزینش کن، حذفش نکن. یک حلقهٔ زیبای خودت بگذار، ولی هیچ‌وقت صفحه را بی‌حلقه رها نکن.

۳. ARIA: کِی لازم است و کِی مضر#

ARIA مجموعه‌ای از attribute هاست که به ماشین‌ها اطلاعاتِ اضافه می‌دهند — مثلِ role (این چیست؟) و aria-label (اسمش چیست؟). به‌نظر می‌رسد هرچه بیشتر، بهتر. ولی حقیقت برعکس است، و این مهم‌ترین درسِ فصل است: اول HTMLِ بومی، ARIA فقط برای پُرکردنِ شکاف.

چرا؟ چون تگ‌های بومی از قبل نقششان را دارند. <button> به ماشین می‌گوید «من دکمه‌ام»، با صفحه‌کلید کار می‌کند و Enter و Space را می‌فهمد — همه رایگان. حالا فرض کن کسی دکمهٔ «دنبال کن» را این‌طور ساخته باشد:

<div class="btn" role="button">دنبال کن</div>

روی صفحه شبیهِ یک دکمه است. ولی در آزمایشگاه با Tab امتحانش کن: فوکوس اصلاً به آن نمی‌رسد؛ ازش رد می‌شود، انگار وجود ندارد. کاربرِ صفحه‌کلید هیچ‌وقت نمی‌تواند «دنبال کن» را بزند.

دو دکمهٔ به‌ظاهر یک‌شکل کنار هم با یک مسیرِ نقطه‌چینِ صفحه‌کلید؛ مسیر روی دکمهٔ توپُرِ سمتِ راست می‌ایستد و دورش یک قابِ ضخیمِ قرمز می‌گیرد، ولی از دکمهٔ خط‌چینِ توخالیِ سمتِ چپ می‌پرد و رد می‌شود بی‌آنکه قابی بگیرد

🔧 اگر کار نکرد: آن role="button" فقط اسمِ «دکمه» را چسبانده، ولی <div> هیچ‌کدام از رفتارهای واقعیِ دکمه را ندارد — نه قابلِ‌فوکوس است، نه Enter را می‌فهمد. راهِ حل ARIAی بیشتر نیست؛ راهِ حل عنصرِ بومیِ درست است. <div role="button"> را با <button> عوض کن:

<button class="btn" type="button">دنبال کن</button>

حالا Tab مستقیم رویش می‌ایستد، حلقهٔ focus می‌گیرد و Enter فعالش می‌کند — همه بدونِ یک attributeِ اضافه. این قاعدهٔ طلایی است: هر جا یک تگِ بومی برای کاری هست، هیچ مقدار ARIA جای آن را نمی‌گیرد.

پس ARIA کِی درست است؟ دقیقاً جایی که HTML حرفی برای گفتن ندارد. دکمهٔ آیکونیِ «هم‌رسانی» متنِ دیدنی ندارد — فقط یک آیکون. screen reader چیزی برای خواندن ندارد. اینجا aria-label شکاف را پُر می‌کند:

<button class="icon-btn" type="button" aria-label="هم‌رسانی پروفایل">

این ARIAی درست است: اسمی می‌دهد که HTML به‌تنهایی نمی‌توانست بدهد.

۴. contrast: متن باید خوانا باشد#

یک رنگِ متنِ کم‌رنگ ممکن است روی صفحهٔ تو قشنگ باشد، ولی برای چشمِ خیلی‌ها — زیرِ نورِ آفتاب، روی صفحهٔ ارزان، با چشمِ خسته — ناخواناست. فاصلهٔ رنگِ متن از پس‌زمینه‌اش را contrast می‌گویند، و باید به‌قدرِ کافی زیاد باشد. بیا کم‌بودنش را ببینیم؛ رنگِ توضیح را به یک خاکستریِ پریده عوض کن:

.desc {
  color: #b8b2a4;
}

چک کن: متنِ توضیح حالا انگار محو شده و به‌سختی خوانده می‌شود. همان متن را با #5a5342 (تیره‌تر) بگذار: بی‌آنکه طراحی به‌هم بریزد، کاملاً خوانا می‌شود. قاعده ساده است: رنگ را برای خوانایی انتخاب کن، نه فقط برای زیبایی — و اگر شک داری، تیره‌ترش کن.

💡 نکته: DevTools مرورگر خودش contrast را برایت اندازه می‌گیرد. روی متن Inspect کن؛ کنارِ color یک عددِ نسبت و یک علامتِ ✓ یا ✗ می‌بینی که می‌گوید به حدِ لازم رسیده یا نه. لازم نیست حدس بزنی.

۵. کد کامل#

اول index.html — دکمهٔ بومی، nav با aria-label که این منو را از منوهای دیگر جدا می‌کند، و دکمهٔ آیکونی با aria-label:

<!-- index.html -->
<!DOCTYPE html>
<html lang="fa" dir="rtl">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>دسترس‌پذیری عمیق</title>
    <link rel="stylesheet" href="styles.css">
  </head>
  <body>
    <main class="card">
      <h1>پروفایلِ آوا</h1>
      <nav class="menu" aria-label="پیمایش پروفایل">
        <ul>
          <li><a href="#posts">پست‌ها</a></li>
          <li><a href="#about">درباره</a></li>
        </ul>
      </nav>
      <p class="desc">این پروفایل را دنبال کن تا پست‌های تازه‌اش را ببینی.</p>
      <footer class="actions">
        <button class="btn" type="button">دنبال کن</button>
        <button class="icon-btn" type="button" aria-label="هم‌رسانی پروفایل">
          <svg class="icon" viewBox="0 0 24 24" width="20" height="20" aria-hidden="true">
            <path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92s2.92-1.31 2.92-2.92-1.31-2.92-2.92-2.92z" fill="currentColor" />
          </svg>
        </button>
      </footer>
    </main>
  </body>
</html>

و styles.css با حلقهٔ focus و رنگِ خوانا:

/* styles.css */
* {
  box-sizing: border-box;
}

:root {
  --bg: #f6edda;
  --card: #ffffff;
  --ink: #2c2114;
  --muted: #5a5342;
  --brand: #2a6b93;
  --line: #d8ccae;
  --focus: #bc3e1c;
}

body {
  margin: 0;
  font-family: Tahoma, sans-serif;
  background-color: var(--bg);
  color: var(--ink);
  padding: 40px;
  line-height: 1.9;
}

.card {
  max-width: 360px;
  margin-inline: auto;
  padding: 24px;
  background-color: var(--card);
  border: 1px solid var(--line);
  border-radius: 16px;
}

.card h1 {
  margin: 0 0 10px;
  font-size: 1.4rem;
}

.menu ul {
  display: flex;
  gap: 16px;
  margin: 0 0 12px;
  padding: 0;
  list-style: none;
}

.menu a {
  color: var(--brand);
  font-weight: 700;
  text-decoration: none;
}

.desc {
  margin: 0 0 18px;
  color: var(--muted);
}

.actions {
  display: flex;
  gap: 10px;
  align-items: center;
  margin: 0;
}

.btn {
  padding: 8px 18px;
  border: 1px solid var(--brand);
  border-radius: 10px;
  background-color: var(--brand);
  color: #ffffff;
  font-family: inherit;
  font-size: 1rem;
  font-weight: 700;
  cursor: pointer;
}

.icon-btn {
  display: inline-flex;
  padding: 8px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background-color: transparent;
  color: var(--brand);
  cursor: pointer;
}

/* har chizi ke focus migirad yek halghe-ye didani darad */
:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}

۶. همیار AI: کمکِ درست، اشتباهِ رایج#

دسترس‌پذیری دقیقاً همان‌جایی است که AI هم بیشترین کمک و هم بیشترین اشتباه را دارد — چون درباره‌اش پُرحرف و مطمئن است، ولی زیاده‌رویِ ARIA اش را فقط وقتی می‌فهمی که خودت قاعده را بلد باشی. سه قانونِ ثابتِ کار با AI در کلِ این خودآموز اینهاست:

🤖 با AI: سه قانون را عیناً به خاطر بسپار: «اول خودت، بعد AI» — اول با معیارهای خودت تصمیم بگیر، بعد نظر AI را به‌عنوان نظرِ دوم بشنو، نه جوابِ نهایی. «خط به خط» — هر چیزی که AI می‌دهد را خط‌به‌خط بخوان و بفهم، هیچ‌وقت یک‌جا کپی نکن. «AI هم اشتباه می‌کند» — و همین الان یک نمونه‌اش را با هم می‌بینیم.

کاربردِ خوب. بهترین استفادهٔ AI اینجا یک بازبینیِ بعد از کارِ خودت است — یک جفت‌چشمِ دوم:

🤖 با AI: بعد از اینکه خودت با Tab صفحه را تست کردی، HTML ات را به دستیار AI ات بده و بپرس: «آیا چیزی در این صفحه هست که کاربرِ صفحه‌کلید به آن نرسد، یا دکمه‌ای که اسمِ خوانا نداشته باشد؟». این کمک می‌کند شکافی را که از چشمت افتاده ببینی — ولی هر پیشنهادش را خودت در آزمایشگاه با Tab بسنج، نه اینکه فقط باور کنی.

کاربردِ اشتباه. حالا نمونه‌ای که AI با اعتمادبه‌نفسِ کامل، صفحه را بدتر می‌کند. اگر بگویی «این صفحه را دسترس‌پذیرتر کن»، خیلی وقت‌ها AI شروع می‌کند به پاشیدنِ ARIA روی همه‌چیز — حتی روی چیزهایی که از قبل بومی و درست‌اند:

<nav class="menu" role="navigation" aria-label="پیمایش پروفایل">
  ...
</nav>
<button class="btn" type="button" role="button" aria-label="دنبال کن">دنبال کن</button>

قاطع توضیح می‌دهد «این‌ها صفحه را دسترس‌پذیرتر می‌کنند». ولی با همان چیزی که در این فصل فهمیدی می‌توانی ردش کنی. <nav> از قبل به ماشین می‌گوید «من ناوبری‌ام»، پس role="navigation" فقط همان حرف را دوباره می‌زند — مثلِ تابلوی «در» روی یک در. <button> از قبل نقشِ دکمه دارد، پس role="button" رویش زیادی است. و آن aria-label="دنبال کن" که عینِ متنِ دیدنیِ دکمه است، بدترین‌شان است: اسمِ دستی‌ای می‌گذارد که اگر فردا متنِ دکمه را عوض کنی، این برچسب پشتِ سرش عوض نمی‌شود و ماشین حرفِ اشتباه می‌زند.

قاعده وارونهٔ چیزی است که به‌نظر می‌رسد: ARIA را برای پُرکردنِ شکاف به‌کار ببر، نه برای تکرارِ چیزی که HTML از قبل می‌گوید. ARIAی درست همان یک aria-label روی دکمهٔ آیکونی بود که متن نداشت. این بهترین درسِ «AI هم اشتباه می‌کند» است: جمله‌ای می‌سازد که کمک‌کننده به‌نظر می‌رسد، ولی وقتی خط‌به‌خط با فهمِ خودت بسنجی‌اش، می‌بینی دارد شلوغی و خطا اضافه می‌کند. اول خودت، بعد AI.

واژه‌های تازهٔ این فصل#

کلمه تلفظ به حروف فارسی یعنی چه
focus order فوکوس اُردِر ترتیبی که Tab بینِ عنصرها می‌پرد؛ از ترتیبِ HTML می‌آید
ARIA آریا attribute هایی که به ماشین‌ها نقش و اسمِ اضافه می‌دهند؛ فقط برای پُرکردنِ شکاف
role رول attributeِ ARIA که می‌گوید یک عنصر «چیست»
aria-label آریا لِیبِل اسمِ خوانا برای عنصری که متنِ دیدنی ندارد، مثلِ دکمهٔ آیکونی
contrast کُنتراست فاصلهٔ رنگِ متن از پس‌زمینه؛ هرچه بیشتر، خواناتر

تمرین‌ها

اول خودت فکر کن یا امتحان کن — بعد اینجا را باز کن.

در فصل بعد#

الان می‌توانی یک صفحه را برای همه باز نگه داری. فصل بعد سراغِ چیزی می‌رویم که کاربر هم مثلِ دسترس‌پذیری حسش می‌کند ولی معمولاً نمی‌بیندش چرا: سرعت. یاد می‌گیری چرا صفحه موقعِ بارگذاری می‌پرد و چطور با width/height جلویش را بگیری، چطور تصویرها را سبک کنی، و چطور CSSِ بی‌استفاده را پیدا و حذف کنی.

به آخر این فصل رسیدی!

اگر ساختی و جواب داد، این دکمه مال توست.