Skip to content

Sortable table

For rows that must be compared field by field, and reordered on whichever column the reader cares about.

Data display

Live preview

Source

<div class="dc-table-wrap">
  <table class="dc-table">
    <caption class="dc-table__caption">협력업체 청구 내역 <span class="dc-table__count">4건</span></caption>
    <thead>
      <tr>
        <th class="dc-table__th" scope="col" aria-sort="ascending"><button class="dc-table__sort" type="button">업체명<span class="dc-table__arrow" aria-hidden="true">▲</span></button></th>
        <th class="dc-table__th" scope="col" aria-sort="none"><button class="dc-table__sort" type="button">상태<span class="dc-table__arrow" aria-hidden="true">▲</span></button></th>
        <th class="dc-table__th dc-table__th--num" scope="col" aria-sort="none"><button class="dc-table__sort" type="button">청구금액<span class="dc-table__arrow" aria-hidden="true">▲</span></button></th>
        <th class="dc-table__th" scope="col" aria-sort="none"><button class="dc-table__sort" type="button">청구일<span class="dc-table__arrow" aria-hidden="true">▲</span></button></th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <th class="dc-table__rowhead" scope="row">금성안전</th>
        <td class="dc-table__td">승인</td>
        <td class="dc-table__td dc-table__td--num">7,905,000</td>
        <td class="dc-table__td">2026-03-09</td>
      </tr>
      <tr>
        <th class="dc-table__rowhead" scope="row">대림전기</th>
        <td class="dc-table__td">승인</td>
        <td class="dc-table__td dc-table__td--num">1,240,000</td>
        <td class="dc-table__td">2026-03-02</td>
      </tr>
      <tr>
        <th class="dc-table__rowhead" scope="row">서진토건</th>
        <td class="dc-table__td">검토 중</td>
        <td class="dc-table__td dc-table__td--num">860,500</td>
        <td class="dc-table__td">2026-03-04</td>
      </tr>
      <tr>
        <th class="dc-table__rowhead" scope="row">한울설비</th>
        <td class="dc-table__td">반려</td>
        <td class="dc-table__td dc-table__td--num">312,000</td>
        <td class="dc-table__td">2026-03-05</td>
      </tr>
    </tbody>
  </table>
</div>
.dc-table-wrap { overflow-x: auto; max-width: 100%; }
.dc-table {
  width: 100%; border-collapse: collapse;
  font-family: var(--dc-font); font-size: 13px;
  color: var(--dc-text);
}
.dc-table__caption {
  padding-bottom: var(--dc-2);
  text-align: left; font-size: 14px; font-weight: 700; color: var(--dc-text);
}
.dc-table__count { font-weight: 400; color: var(--dc-text-mute); }
.dc-table__th {
  padding: 0; background: var(--dc-sunken);
  border-bottom: 1px solid var(--dc-line-bold);
  text-align: left; white-space: nowrap;
  font-size: 12px; color: var(--dc-text-sub);
}
.dc-table__sort {
  display: flex; align-items: center; gap: var(--dc-1);
  width: 100%; min-height: 40px;
  padding: var(--dc-2) var(--dc-3);
  border: 0; background: transparent; color: inherit;
  font: inherit; font-weight: 700; text-align: left;
  cursor: pointer;
}
.dc-table__sort:hover { color: var(--dc-text); background: color-mix(in srgb, var(--dc-brand) 8%, transparent); }
.dc-table__th--num .dc-table__sort { justify-content: flex-end; }
.dc-table__arrow { font-size: 9px; color: var(--dc-text-mute); }
.dc-table__th[aria-sort="none"] .dc-table__arrow { opacity: 0; }
.dc-table__th:hover .dc-table__arrow,
.dc-table__sort:focus-visible .dc-table__arrow { opacity: 1; }
.dc-table__th[aria-sort="ascending"] .dc-table__arrow,
.dc-table__th[aria-sort="descending"] .dc-table__arrow { color: var(--dc-brand-text); }
.dc-table__th[aria-sort="descending"] .dc-table__arrow { transform: rotate(180deg); }
.dc-table__rowhead,
.dc-table__td {
  padding: var(--dc-3);
  border-bottom: 1px solid var(--dc-line);
  text-align: left; vertical-align: top;
}
.dc-table__rowhead { font-weight: 700; white-space: nowrap; }
.dc-table__td { color: var(--dc-text-sub); }
.dc-table__td--num {
  text-align: right; color: var(--dc-text);
  font-variant-numeric: tabular-nums;
}
.dc-table tbody tr:hover .dc-table__rowhead,
.dc-table tbody tr:hover .dc-table__td { background: var(--dc-sunken); }
.dc-table--compact .dc-table__rowhead,
.dc-table--compact .dc-table__td { padding: var(--dc-2) var(--dc-3); }
.dc-table--compact .dc-table__sort { min-height: 32px; }
.dc-table__empty {
  padding: var(--dc-8) var(--dc-3);
  border-bottom: 1px solid var(--dc-line);
  text-align: center; color: var(--dc-text-sub); line-height: 1.7;
}

How to use it

  • Put the sort control inside the header cell and announce the current key with aria-sort
  • Right-align numeric columns and set tabular-nums so the digits line up
  • The cell that names the row is a th with scope="row"
  • On narrow screens scroll the table sideways instead of dropping columns

Watch out for

  • Flipping the arrow is not enough — without aria-sort nobody hears what it is sorted by
  • Keep the caption: a table should say what it lists before it lists it

Next