جدول‌های واکنش‌گرا

جدول‌های داده بزرگ اغلب در صفحه‌نمایش‌های کوچک ناخوانا می‌شوند زیرا ستون‌ها سرریز می‌شوند یا بیش‌از حد خوانایی کوچک می‌شوند. این راهنما نشان می‌دهد که چگونه از موقعیت‌یابی چسبنده استفاده کنید تا سرصفحه‌ها هنگام پیمایش قابل‌مشاهده بمانند و چگونه جدول را به چیدمان «پشته‌ای» سازگار با تلفن همراه تبدیل کنید وقتی فضا محدود است.

هسته اصلی جدول واکنش‌گرا حفظ رابطه بین سلول‌های داده و سرصفحه‌های آن‌ها است.

  1. پایه معنایی: از عناصر استاندارد <table> با عناصر <thead>، <tbody>، و <th> استفاده کنید.
  2. بافت چسبان: position: sticky را هم به سرصفحه ستون‌ها و هم به سرصفحه ردیف‌ها اعمال کنید. این کار تضمین می‌کند که مهم نیست کاربر چقدر در هر جهتی پیمایش می‌کند، او هرگز زمینه آنچه داده‌ها نشان می‌دهند را ازدست نمی‌دهد.
  3. تبدیل‌های تطبیقی: از پُرسمان‌های @container به‌جای پُرسمان‌های @media استفاده کنید. این کار به جدول اجازه می‌دهد تا براساس عرض خودش (مثلاً وقتی در نوار کناری یا ابزاره داشبورد باریک قرار می‌گیرد) به‌جای کل نمای دید، سازگار شود.
  4. تزریق برچسب: در چیدمان پشته‌ای، <thead> پنهان است و سرایندهای دردسترس بااستفاده از شبه‌عناصر ::before و متغیرهای CSS به هر سلول تزریق می‌شوند.

مراحل پیاده‌سازی

۱. نشانه‌گذاری و چیدمان

جدولتان را با سرصفحه‌های معنایی استاندارد ساختاربندی کنید. از .table-wrapper برای مدیریت سرریز و ارائه محفظه‌ای برای پُرسمان‌ها استفاده کنید. مشخصه tabindex="0" را به محفظه اضافه کنید تا مطمئن شوید افرادی که فقط از صفحه‌کلید استفاده می‌کنند می‌توانند به‌طور مداوم جدول را پیمایش کنند.

به پوشاننده مشخصه role="region" بدهید تا هدف آن را منتقل کند. بااستفاده از aria-labelledby (ارجاع به مشخصه id عنصر موجودی که جدول را نام‌گذاری می‌کند/ برای آن زیرنویس می‌گذارد) یا مشخصه aria-label، نامی دسترس‌پذیر برای آن ارائه دهید.

<div tabindex="0" role="region" aria-labelledby="employee-table-caption" class="table-wrapper">
  <table>
    <caption id="employee-table-caption">Current employee directory</caption>
    <thead>
      <tr>
        <th>Employee</th>
        <th>Role</th>
        <th>Status</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <!-- Row header remains sticky horizontally on desktop, 
             and becomes a sticky card header on mobile -->
        <th>Alex Rivera</th>
        <td>Engineer</td>
        <td>Active</td>
      </tr>
    </tbody>
  </table>
</div>

۲. پیکربندی سرصفحه‌های چسبان

پیمایش را فعال کنید و سرصفحه‌ها را چسبان کنید. از ویژگی‌های منطقی و مقادیر صریح z-index برای مدیریت ترتیب پشته‌سازی استفاده کنید.

.table-wrapper {
  overflow: auto;
  max-inline-size: 100%;
  max-block-size: min(500px, 80vh);
  container-type: inline-size;
}

table {
  border-collapse: separate;
  border-spacing: 0;
}

/* Sticky column headers (desktop) */
thead {
  position: sticky;
  inset-block-start: 0;
  z-index: 1; /* Above sticky row headers */
}

/* Sticky row headers (desktop) */
tbody th {
  position: sticky;
  inset-inline-start: 0;
  background: white; /* Required to cover background during scroll */
}

۳. چیدمان پشته‌ای واکنش‌گرا

وقتی فضا محدود است، ردیف سرصفحه اصلی را پنهان کنید و جدول را به کارت تبدیل کنید. برچسب‌ها را بااستفاده از شبه‌عناصر تزریق کنید. فقط برای محتوای غیرنوشتاری نوشتار جایگزین ارائه دهید، درغیراین‌صورت ممکن است صفحه‌خوان‌ها هم محتوای نوشتاری و هم نام دسترس‌پذیر را اعلام کنند.

@container (width < 600px) {
  table, thead, tbody, tr, th, td {
    display: block;
  }

  thead {
    /* MANDATORY: Hide column header from layout and screen readers. */
    display: none
  }

  tr {
    margin-block-end: 1.5rem;
    border: 1px solid #ccc;
  }

  /* Sticky row headers at the top of each block/card */
  tbody th {
    position: sticky;
    inset-block-start: 0;
    z-index: 2;
    background: #eee;
  }

  /* Define column labels as CSS variables (often set via JS) */
  table {
    --label-1: "Employee";
    --label-2: "Role";
    --label-3: "Status";
  }

  /* Inject labels */
  td:nth-child(2)::before {
    /* MANDATORY: If using non-text content, provide the accessible name (after the /) which ensures screen readers hear the text alternative. */
    content: var(--label-2) ": ";
  }
  /* MANDATORY: Map the label for each column */
  td:nth-child(3)::before {
    content: var(--label-3) ": ";
  }
}