컴포넌트

Table

데이터를 행과 열로 표시합니다.

설치

pnpm dlx ply-ui add table

공통 설치 설정을 먼저 적용합니다.

사용법

Header/Body 안에 Row를 구성하고 Head와 Cell을 구분합니다.

Caption으로 표의 목적을 설명하고 열 제목에 scope="col"을 지정합니다.

구성

구성 요소역할
Table행과 열을 묶는 표입니다.
TableCaption표의 이름이나 설명입니다.
TableHeader / TableBody제목 행과 데이터 행 영역입니다.
TableRow하나의 행입니다.
TableHead제목 셀입니다. scope로 행 또는 열 제목을 지정합니다.
TableCell데이터 셀입니다.

좁은 영역에서의 동작

Table은 가로 스크롤 영역을 기본으로 포함합니다. 넓은 표가 페이지 전체를 밀어내지 않고, 키보드로 스크롤 영역에 접근할 수 있습니다. containerProps는 바깥 영역, 나머지 속성과 ref는 실제 table에 전달합니다.

구현 코드

components/ui/table.tsx
table.tsx
import type { ComponentProps } from "react";
import { cx } from "../../lib/cx";
import "./table.css";

export type TableProps = ComponentProps<"table"> & {
  /** 표의 의미와 ref는 table에 남기고, 가로 스크롤 영역만 별도로 설정합니다. */
  containerProps?: ComponentProps<"div">;
};
export function Table({ className, containerProps, ...props }: TableProps) {
  return (
    <div
      role="region"
      aria-label={props["aria-label"] ?? "표 가로 스크롤"}
      tabIndex={0}
      {...containerProps}
      className={cx("rbx-table-container", containerProps?.className)}
    >
      <table {...props} className={cx("rbx-table", className)} />
    </div>
  );
}

export function TableHeader({ className, ...props }: ComponentProps<"thead">) {
  return <thead {...props} className={cx("rbx-table-header", className)} />;
}

export function TableBody({ className, ...props }: ComponentProps<"tbody">) {
  return <tbody {...props} className={cx("rbx-table-body", className)} />;
}

export function TableRow({ className, ...props }: ComponentProps<"tr">) {
  return <tr {...props} className={cx("rbx-table-row", className)} />;
}

export function TableHead({ className, ...props }: ComponentProps<"th">) {
  return <th {...props} className={cx("rbx-table-head", className)} />;
}

export function TableCell({ className, ...props }: ComponentProps<"td">) {
  return <td {...props} className={cx("rbx-table-cell", className)} />;
}

export function TableCaption({
  className,
  ...props
}: ComponentProps<"caption">) {
  return <caption {...props} className={cx("rbx-table-caption", className)} />;
}
components/ui/table.css
table.css
@layer theme, base, components, utilities;
@layer components.ply-base, components.ply;

@layer components.ply {
  /* table — semantic HTML composition. */
  .rbx-table {
    width: 100%;
    border-collapse: collapse;
    text-align: start;
  }
  .rbx-table-head,
  .rbx-table-cell {
    white-space: nowrap;
    padding: 14px 16px;
    border-bottom: 1px solid var(--rbx-color-stroke-default);
  }
  .rbx-table-head {
    /* th의 브라우저 기본 가운데 정렬을 표의 시작 정렬에 맞춥니다. */
    text-align: inherit;
    color: var(--rbx-color-content-muted);
    font-size: 0.875rem;
    font-weight: 600;
  }
  .rbx-table-caption {
    text-align: start;
    padding: 12px;
    color: var(--rbx-color-content-default);
    font-size: 0.875rem;
  }
  .rbx-table-container {
    width: 100%;
    max-width: 100%;
    min-width: 0;
    overflow-x: auto;
    overscroll-behavior-x: contain;
  }
  .rbx-table-container:focus-visible {
    outline-offset: -3px;
  }
}

목차