{
  "$schema": "https://www.digitaltableteur.com/schemas/contract-spec/1.0/contract.schema.json",
  "specVersion": "1.0",
  "name": "Table",
  "status": "stable",
  "description": "The composable table root: a scroll wrapper around a semantic table with density, striping, and a sticky header. Compose with the sibling TableRow, TableHeaderCell, and TableCell components. The presentational layer beneath DataTable; pair with the useTable* hooks for behavior.",
  "package": {
    "name": "@digitaltableteur/react",
    "import": "import { Table } from \"@digitaltableteur/react\";"
  },
  "props": {
    "caption": {
      "type": "string",
      "optional": true,
      "description": "Accessible table name, rendered as a `<caption>`. Optional only when the\ntable is named by aria-labelledby or aria-label."
    },
    "hideCaption": {
      "type": "boolean",
      "optional": true,
      "description": "Visually hide the caption while keeping the accessible name. Defaults to\n`false`."
    },
    "size": {
      "type": "union",
      "values": [
        "sm",
        "md",
        "lg"
      ],
      "optional": true,
      "description": "Density scale. Defaults to `md`."
    },
    "striped": {
      "type": "boolean",
      "optional": true,
      "description": "Alternating row surfaces. Defaults to `false`."
    },
    "stickyHeader": {
      "type": "boolean",
      "optional": true,
      "description": "Pin the header row while the table body scrolls. Defaults to `false`."
    },
    "wrapperClassName": {
      "type": "string",
      "optional": true,
      "description": "Class applied to the scroll wrapper."
    },
    "stackOnNarrow": {
      "type": "boolean",
      "optional": true,
      "description": "Below a 40rem container width, stack each body row as a block instead of\nsqueezing columns, with every cell prefixed by its TableCell label. Table\nsets role=table and the row and cell components add their own roles, so\nassistive technology keeps table semantics; give thead and tbody\nrole=\"rowgroup\" yourself. Defaults to `false`."
    }
  },
  "rules": [
    {
      "id": "table-needs-name",
      "when": {
        "caption": {
          "absent": true
        }
      },
      "requireAnyOf": [
        "aria-labelledby",
        "aria-label"
      ],
      "reason": "A table without a caption has no accessible name. Pass caption, or aria-labelledby pointing at a visible heading, e.g. <Table aria-labelledby=\"usage-heading\">.",
      "severity": "error",
      "evidence": "Table.tsx development warning (!caption && !aria-labelledby && !aria-label)"
    }
  ],
  "accessibility": {
    "criteria": [
      {
        "id": "axe-no-violations",
        "statement": "Required stories produce no axe violations at the configured severity.",
        "verification": "automated",
        "wcag": [
          "2.5.8"
        ],
        "check": "npm run test:stories:matrix:ci",
        "evidence": [
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-default.dark.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:39:57.356Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-default.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:20:40.226Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-default.light.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:33:49.850Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-example.dark.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:39:58.084Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-example.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:20:40.780Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-example.light.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:33:50.707Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-forced-colors.dark.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:39:58.804Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-forced-colors.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:20:41.336Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-forced-colors.light.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:33:51.537Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-playground.dark.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:39:57.712Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-playground.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:20:40.506Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-playground.light.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:33:50.274Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-stacked-on-narrow.dark.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:39:58.455Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-stacked-on-narrow.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:20:41.059Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-stacked-on-narrow.light.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:33:51.132Z",
            "passed": true,
            "runner": "local-recapture"
          }
        ]
      },
      {
        "id": "accessibility-tree",
        "statement": "The accessibility tree (role, name, state) matches the committed snapshot for every required story.",
        "verification": "automated",
        "wcag": [
          "1.3.1",
          "4.1.2"
        ],
        "check": "npm run test:stories:matrix:ci",
        "evidence": [
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-default.dark.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:39:57.356Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-default.forced-colors.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:49:28.969Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-default.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:20:40.226Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-default.light.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:33:49.850Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-example.dark.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:39:58.084Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-example.forced-colors.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:49:29.513Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-example.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:20:40.780Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-example.light.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:33:50.707Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-forced-colors.dark.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:39:58.804Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-forced-colors.forced-colors.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:49:30.065Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-forced-colors.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:20:41.336Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-forced-colors.light.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:33:51.537Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-playground.dark.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:39:57.712Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-playground.forced-colors.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:49:29.244Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-playground.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:20:40.506Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-playground.light.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:33:50.274Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-stacked-on-narrow.dark.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:39:58.455Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-stacked-on-narrow.forced-colors.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:49:29.790Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-stacked-on-narrow.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:20:41.059Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-stacked-on-narrow.light.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:33:51.132Z",
            "passed": true,
            "runner": "local-recapture"
          }
        ]
      },
      {
        "id": "forced-colors-real-browser",
        "statement": "The component remains legible and operable under a real browser forced-colors: active pass.",
        "verification": "automated",
        "check": "npm run test:stories:hc",
        "evidence": [
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-default.forced-colors.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:49:28.969Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-example.forced-colors.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:49:29.513Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-forced-colors.forced-colors.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:49:30.065Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-playground.forced-colors.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:49:29.244Z",
            "passed": true,
            "runner": "local-recapture"
          },
          {
            "artifact": "../../../nextjs-app/shared/components/Table/__a11y-evidence__/data-display-table-stacked-on-narrow.forced-colors.json",
            "sourceCommit": "930c562b591b6d9967e1e8c214899052624d9a2f",
            "capturedAt": "2026-11-03T22:49:29.790Z",
            "passed": true,
            "runner": "local-recapture"
          }
        ]
      },
      {
        "id": "keyboard-contract",
        "statement": "Every documented keyboard interaction works as specified.",
        "verification": "unverified",
        "wcag": [
          "2.1.1",
          "2.1.2"
        ],
        "note": "No exportable evidence record for this check; re-run the check to capture one."
      },
      {
        "id": "aria-requirements",
        "statement": "Documented ARIA roles, states, and properties are present on the rendered output.",
        "verification": "unverified",
        "wcag": [
          "4.1.2"
        ],
        "note": "No exportable evidence record for this check; re-run the check to capture one."
      },
      {
        "id": "human-a11y-review",
        "statement": "A human completed an end-to-end accessibility review of this component.",
        "verification": "manual",
        "note": "2026-07-27 — native table semantics, caption naming, aria-sort on sortable headers, decorative sort caret, and axe result reviewed in unit tests. 2026-08-04 — Phase 1 pass: light/dark/forced-colors verified in a real browser; axe clean across all stories x 3 modes; AT-tree and real-browser forced-colors evidence captured; keyboard sort cycle and selection driven by play stories at the Table composition level. 2026-10-15: promoted to stable. Production consumers per audit:consumers; evidence recaptured in light, dark and forced-colors (real Chromium forced-colors pass) against 1b3b7d94a, every record passing with axe clean.",
        "reviewedAt": "2026-11-04"
      }
    ]
  },
  "governance": {
    "owner": "design-system",
    "lastReviewed": "2026-11-04",
    "note": "Reviewed by Petri Lahdelma; prepared by Claude from a code review on 2026-11-03"
  },
  "source": [
    "../../../nextjs-app/shared/components/Table/Table.module.css",
    "../../../nextjs-app/shared/components/Table/Table.tsx"
  ]
}
