{
  "name": "Table",
  "source": "src/components/Table.tsx",
  "propsType": "TableProps",
  "import": "import { Table } from 'ionbase-ui';",
  "status": "stable",
  "summary": "A data table. Table owns the scroll container and density; rows and cells are composed.",
  "useWhen": [
    "showing records with more than one attribute worth comparing across rows"
  ],
  "useInstead": [
    {
      "when": "each item is a self-contained summary rather than a row of fields",
      "use": "FullCard"
    },
    {
      "when": "each row is one record read across — a label, a line under it, a badge — with nothing to compare down a column",
      "use": "List",
      "why": "a table claims columns and a header; a one-column table is announced as one and has no row actions a keyboard can reach"
    },
    {
      "when": "the rows nest — a total and the rows it is made of, each with the same columns",
      "use": "TreeGrid",
      "why": "indented rows in a Table carry no level and no expand keys; a TreeGrid is the treegrid pattern, drawn as a Table"
    },
    {
      "when": "it is a layout grid rather than data",
      "use": "CSS grid",
      "why": "a <table> announces row and column semantics that a layout does not have"
    }
  ],
  "composition": {
    "order": [
      "Table",
      "TableHead",
      "TableRow",
      "TableCell"
    ],
    "note": "Table > TableHead/TableBody > TableRow > TableCell, matching how Figma layers Table Row > Table Cell > Cell Text. TableHead and TableBody take no props of their own; they place the rows and give TableCell its head-or-body context. For a long list, `maxHeight` scrolls the rows inside the table with the header held at its top; for a wide one, `stickyFirstColumn` holds the first column — and any toggle and checkbox cells before it — while the rest scrolls sideways. Both hold to the table's own scroll region, never the page.",
    "example": "<Table aria-label=\"Users\"><TableHead><TableRow><TableCell scope=\"col\">Name</TableCell></TableRow></TableHead><TableBody><TableRow><TableCell>Ada</TableCell></TableRow></TableBody></Table>"
  },
  "variants": {
    "density": {
      "compact": {
        "use": "dense operational tables where row count matters more than comfort"
      },
      "default": {
        "use": "the default"
      },
      "relaxed": {
        "use": "short tables where each row deserves attention"
      }
    }
  },
  "a11y": {
    "guarantees": [
      "the scroll container is keyboard reachable, which overflow alone does not provide (WCAG 2.1.1)",
      "`maxHeight` holds the header at the top of the table's scroll region; its bottom rule is drawn by the cells, so it moves with them",
      "`stickyFirstColumn` holds the first column, and the toggle and checkbox cells before it, each placed after the one before; a held cell takes its row's background, so nothing shows through",
      "nothing focused is left under a held header or column (WCAG 2.4.11): the region's scroll padding is their measured size, and a control half under them is scrolled clear on focus",
      "the held column's edge shows a shadow only once something has scrolled under it",
      "rows that arrive later — a page change, a row opening — are measured and held too; an open row's detail spans everything and is not held"
    ],
    "requires": [
      "`aria-label` or `aria-labelledby` on Table — it names the scroll region, and the container takes no props of its own, so this is the only place the name can live",
      "`scope=\"col\"` on header cells",
      "an accessible name on every row-selection Checkbox — those have no visible label by design"
    ],
    "notes": [
      "Prefer a visible <caption> referenced by aria-labelledby when the table's name should also be seen.",
      "The header cannot hold to the page: the table's region scrolls sideways, and a sticky cell sticks to its nearest scroller. Give it `maxHeight` instead."
    ]
  },
  "antiPatterns": [
    {
      "dont": "<Table> with no aria-label",
      "why": "the scroll region is announced with no name, and a keyboard user reaching it is told nothing about what they have entered"
    },
    {
      "dont": "header cells without scope",
      "why": "screen readers cannot associate data cells with their column"
    },
    {
      "dont": "wrapping Table in your own overflow container",
      "why": "Table already owns the scroll container and makes it focusable; a second one is unreachable by keyboard"
    },
    {
      "dont": "row-selection checkboxes with no aria-label",
      "why": "every row announces an unnamed checkbox"
    },
    {
      "dont": "position: sticky on your own <thead> cells",
      "do": "`maxHeight` on Table",
      "why": "the header sticks to the table's scroll region, which does not scroll up and down without a height, and its rule stays behind with border-collapse; focus can also land under it"
    },
    {
      "dont": "stickyFirstColumn on a table that fits its space",
      "why": "nothing scrolls under the column; it only adds layers. Hold the column where the table is wider than its space — a phone, a panel beside it"
    }
  ],
  "missingStates": {
    "note": "Table has no empty, loading or error state of its own — those are the caller's, and they are the states most often forgotten. Pattern coverage is planned; see docs/agent-readiness-plan.md phase 4a."
  },
  "stylesheet": "src/styles/table.css",
  "tokens": [
    "--border-default",
    "--border-focus",
    "--border-strong",
    "--border-width-default",
    "--border-width-thick",
    "--font-family-sans",
    "--font-weight-medium",
    "--font-weight-regular",
    "--icon-default",
    "--icon-interactive",
    "--icon-secondary",
    "--icon-size-sm",
    "--icon-tertiary",
    "--ion-duration-base",
    "--ion-ease-out",
    "--ion-shadow-shadow-md",
    "--ion-table-head-height",
    "--ion-table-held-width",
    "--ion-table-sticky-left",
    "--radius-full",
    "--radius-md",
    "--radius-xs",
    "--ring-focus",
    "--spacing-16",
    "--spacing-20",
    "--spacing-24",
    "--spacing-6",
    "--spacing-8",
    "--surface-default",
    "--surface-hover",
    "--surface-page",
    "--surface-primary-subtle",
    "--text-default",
    "--text-link",
    "--text-secondary",
    "--type-body-sm",
    "--type-body-sm-line-height"
  ],
  "props": {
    "density": {
      "type": "TableDensity | undefined",
      "required": false,
      "origin": "own",
      "description": "Matches Figma's `Density` variant on `Table Cell` / `Table Row`.",
      "values": [
        "default",
        "compact",
        "relaxed"
      ]
    },
    "isStriped": {
      "type": "boolean | undefined",
      "required": false,
      "origin": "own",
      "description": "Alternating row background, read from the row's position, not a prop\nrepeated on every `TableRow`."
    },
    "maxHeight": {
      "type": "string | number | undefined",
      "required": false,
      "origin": "own",
      "description": "Scroll the rows inside the table past this height, with the header held\nat the top — a number is pixels, a string any CSS length (`\"60vh\"`).\nThe header sticks to the table's own scroll region, not the page: that\nregion already scrolls sideways, and a sticky cell sticks to the nearest\nscroller."
    },
    "stickyFirstColumn": {
      "type": "boolean | undefined",
      "required": false,
      "origin": "own",
      "description": "Hold the first column in place while the table scrolls sideways — with\nthe toggle and checkbox cells before it, when the rows have them. For a\ntable wider than its space whose rows are named by their first column."
    }
  },
  "propCounts": {
    "own": 4,
    "aria": 0,
    "dom": 287,
    "other": 0
  }
}
