Data table

데이터 테이블

Rows and columns of data, often sortable by clicking a header and selectable row by row.

Also known as: TableData gridGrid view
···
html
<table class="dt">
  <thead><tr><th scope="col" id="dth" aria-sort="ascending"><button>이름 <i>↑</i></button></th><th scope="col">역할</th><th scope="col">상태</th></tr></thead>
  <tbody id="dtb">
    <tr><td>김서연</td><td>디자이너</td><td><span class="pill on">활성</span></td></tr>
    <tr><td>박도윤</td><td>개발자</td><td><span class="pill on">활성</span></td></tr>
    <tr><td>이하은</td><td>PM</td><td><span class="pill off">휴면</span></td></tr>
    <tr><td>최서준</td><td>디자이너</td><td><span class="pill on">활성</span></td></tr>
  </tbody>
</table>
css
.dt{border-collapse:collapse;width:min(280px,94%);font-size:11px}
.dt th{text-align:left;padding:7px 9px;border-bottom:1px solid var(--line);color:var(--muted);font-weight:600}
.dt th button{border:0;background:none;color:inherit;font:inherit;cursor:pointer;display:flex;gap:4px;align-items:center}
.dt th i{font-style:normal;color:var(--accent);transition:transform .2s}
.dt td{padding:7px 9px;border-bottom:1px solid var(--line);color:var(--fg)}
.pill{font-size:9px;font-weight:700;padding:2px 7px;border-radius:999px}
.pill.on{background:color-mix(in srgb, #18c29c 20%, transparent);color:#0f9d78}
.pill.off{background:var(--line);color:var(--muted)}
tr{transition:transform .3s ease}
js
const tbody = document.getElementById('dtb'), th = document.getElementById('dth'), icon = th.querySelector('i');
let asc = true;
function sortRows() {
  const rows = [...tbody.querySelectorAll('tr')];
  rows.sort((a, b) => {
    const av = a.children[0].textContent, bv = b.children[0].textContent;
    return asc ? av.localeCompare(bv, 'ko') : bv.localeCompare(av, 'ko');
  });
  rows.forEach((r) => tbody.appendChild(r));
  th.setAttribute('aria-sort', asc ? 'ascending' : 'descending');
  icon.textContent = asc ? '↑' : '↓';
}
sortRows();
th.querySelector('button').addEventListener('click', () => { asc = !asc; sortRows(); });
setInterval(() => { asc = !asc; sortRows(); }, 1800);

Don't fake a table with divs — use a real <table>/<th scope="col">, since only genuine table markup lets a screen reader announce a cell's position, like "row 3, column 2, Name column." A sortable header wraps the label in a button and carries aria-sort="ascending"/"descending"/"none" on the th itself.

It's often compared with a card list — a data table keeps several attributes (name, role, status, date…) aligned in columns for easy comparison, while a card list shows each item more richly with an image and description. With many rows, it's usually paired with pagination.

On narrow screens, rather than shrinking every column, a common responsive pattern folds less-important columns away and stacks each row like a small vertical card.