data-table-component
the brief, redacted

Design a dense data table with sorting affordances and a selected row state. Tests whether a model can make information density feel calm rather than cramped.

559 chars · 82 words · fingerprint 3b4a4ef0b3ee

raw output — byte exact

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Deployment Records</title>
  <style>
    :root {
      color-scheme: light;
      --page: #f3f4f6;
      --surface: #ffffff;
      --border: #dfe3e8;
      --border-soft: #e8ebef;
      --text: #20242b;
      --muted: #68707d;
      --subtle: #8a929e;
      --header: #f7f8fa;
      --hover: #f5f7f9;
      --selected: #edf5ff;
      --selected-border: #2f6fbd;
      --green: #237a4b;
      --green-bg: #ebf7f0;
      --blue: #2468a9;
      --blue-bg: #edf5fc;
      --red: #b33a3a;
      --red-bg: #fceeee;
      --amber: #986b12;
      --amber-bg: #fff6df;
      --gray: #606975;
      --gray-bg: #f0f2f4;
      --purple: #7150a5;
      --purple-bg: #f3eefb;
      --shadow: 0 1px 2px rgba(18, 25, 38, 0.04);
    }

    * {
      box-sizing: border-box;
    }

    body {
      margin: 0;
      min-width: 1120px;
      min-height: 100vh;
      padding: 64px;
      background: var(--page);
      color: var(--text);
      font-family: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
      font-size: 13px;
      -webkit-font-smoothing: antialiased;
    }

    .table-card {
      width: 1100px;
      margin: 0 auto;
      overflow: hidden;
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: 8px;
      box-shadow: var(--shadow);
    }

    .table-toolbar {
      height: 58px;
      display: flex;
      align-items: center;
      justify-content: space-between;
      padding: 0 18px;
      border-bottom: 1px solid var(--border);
    }

    .title-group {
      display: flex;
      align-items: baseline;
      gap: 9px;
    }

    h1 {
      margin: 0;
      font-size: 15px;
      font-weight: 650;
      letter-spacing: -0.01em;
    }

    .record-count {
      color: var(--muted);
      font-size: 12px;
    }

    .environment {
      display: inline-flex;
      align-items: center;
      gap: 7px;
      height: 28px;
      padding: 0 10px;
      color: #454d58;
      background: #fafbfc;
      border: 1px solid var(--border);
      border-radius: 5px;
      font-size: 12px;
      font-weight: 550;
    }

    .environment-dot {
      width: 7px;
      height: 7px;
      border-radius: 50%;
      background: #237a4b;
      box-shadow: 0 0 0 2px #e1f2e8;
    }

    .table-wrap {
      overflow: hidden;
    }

    table {
      width: 100%;
      border-spacing: 0;
      border-collapse: separate;
      table-layout: fixed;
    }

    col.status { width: 150px; }
    col.service { width: 220px; }
    col.region { width: 154px; }
    col.actor { width: 190px; }
    col.duration { width: 112px; }
    col.timestamp { width: 274px; }

    thead th {
      height: 38px;
      padding: 0 14px;
      color: #606975;
      background: var(--header);
      border-bottom: 1px solid var(--border);
      text-align: left;
      font-size: 11px;
      font-weight: 650;
      letter-spacing: 0.025em;
      text-transform: uppercase;
      white-space: nowrap;
    }

    thead th:first-child,
    tbody td:first-child {
      padding-left: 18px;
    }

    thead th:last-child,
    tbody td:last-child {
      padding-right: 18px;
    }

    .sort-button {
      width: calc(100% + 12px);
      height: 30px;
      margin-left: -6px;
      padding: 0 6px;
      display: flex;
      align-items: center;
      gap: 6px;
      color: #39414c;
      background: transparent;
      border: 0;
      border-radius: 4px;
      font: inherit;
      font-weight: 700;
      letter-spacing: inherit;
      text-transform: inherit;
      cursor: pointer;
    }

    .sort-button:hover {
      background: #eceff2;
    }

    .sort-button:focus-visible {
      outline: 2px solid #3b78bd;
      outline-offset: 1px;
    }

    .sort-icon {
      width: 13px;
      height: 13px;
      color: var(--selected-border);
      transition: transform 140ms ease;
    }

    .sort-button[data-direction="ascending"] .sort-icon {
      transform: rotate(180deg);
    }

    tbody tr {
      position: relative;
      background: var(--surface);
      transition: background-color 100ms ease;
      cursor: default;
    }

    tbody tr:hover,
    tbody tr.is-hovered {
      background: var(--hover);
    }

    tbody tr.is-selected {
      background: var(--selected);
    }

    tbody tr.is-selected:hover {
      background: #e7f1fd;
    }

    tbody td {
      height: 52px;
      padding: 0 14px;
      border-bottom: 1px solid var(--border-soft);
      color: #313741;
      vertical-align: middle;
      white-space: nowrap;
    }

    tbody tr:last-child td {
      border-bottom: 0;
    }

    tbody tr.is-selected td:first-child {
      position: relative;
    }

    tbody tr.is-selected td:first-child::before {
      content: "";
      position: absolute;
      top: -1px;
      bottom: -1px;
      left: 0;
      width: 3px;
      background: var(--selected-border);
    }

    .status-pill {
      display: inline-flex;
      align-items: center;
      gap: 6px;
      min-height: 24px;
      padding: 3px 8px 3px 6px;
      border: 1px solid transparent;
      border-radius: 999px;
      font-size: 11.5px;
      font-weight: 620;
      line-height: 1;
    }

    .status-icon {
      width: 14px;
      height: 14px;
      flex: 0 0 auto;
    }

    .succeeded {
      color: var(--green);
      background: var(--green-bg);
      border-color: #d3ebdc;
    }

    .deploying {
      color: var(--blue);
      background: var(--blue-bg);
      border-color: #d7e7f5;
    }

    .failed {
      color: var(--red);
      background: var(--red-bg);
      border-color: #f2d5d5;
    }

    .queued {
      color: var(--amber);
      background: var(--amber-bg);
      border-color: #f1e3b9;
    }

    .canceled {
      color: var(--gray);
      background: var(--gray-bg);
      border-color: #dde1e5;
    }

    .rolled-back {
      color: var(--purple);
      background: var(--purple-bg);
      border-color: #e1d7f1;
    }

    .service-cell {
      display: flex;
      min-width: 0;
      flex-direction: column;
      justify-content: center;
      gap: 3px;
    }

    .service-name {
      overflow: hidden;
      color: #252b33;
      font-weight: 620;
      text-overflow: ellipsis;
    }

    .revision {
      color: var(--subtle);
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
      font-size: 11px;
    }

    .region-name {
      color: #414852;
    }

    .region-code {
      margin-left: 5px;
      color: var(--subtle);
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
      font-size: 11px;
    }

    .actor-cell {
      display: flex;
      align-items: center;
      gap: 8px;
      min-width: 0;
    }

    .avatar {
      width: 24px;
      height: 24px;
      display: inline-flex;
      flex: 0 0 auto;
      align-items: center;
      justify-content: center;
      color: #53606e;
      background: #eef1f4;
      border: 1px solid #dce1e6;
      border-radius: 50%;
      font-size: 9px;
      font-weight: 750;
      letter-spacing: 0.02em;
    }

    .avatar.bot {
      color: #4f6190;
      background: #edf0f8;
      border-color: #d9deed;
    }

    .actor-name {
      overflow: hidden;
      text-overflow: ellipsis;
    }

    .duration,
    time {
      color: #515965;
      font-variant-numeric: tabular-nums;
    }

    .table-footer {
      height: 42px;
      display: flex;
      align-items: center;
      justify-content: space-between;
      padding: 0 18px;
      color: var(--muted);
      background: #fafbfc;
      border-top: 1px solid var(--border);
      font-size: 11.5px;
    }

    .selection-note {
      color: #4f6682;
    }

    kbd {
      min-width: 20px;
      height: 20px;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      margin-left: 5px;
      padding: 0 5px;
      color: #606975;
      background: #fff;
      border: 1px solid #d8dde3;
      border-bottom-color: #c7cdd4;
      border-radius: 4px;
      box-shadow: 0 1px 0 #dfe3e7;
      font-family: inherit;
      font-size: 10px;
    }
  </style>
</head>
<body>
  <main class="table-card" aria-labelledby="deployments-title">
    <header class="table-toolbar">
      <div class="title-group">
        <h1 id="deployments-title">Deployments</h1>
        <span class="record-count">8 records</span>
      </div>
      <div class="environment" aria-label="Environment: Production">
        <span class="environment-dot" aria-hidden="true"></span>
        Production
      </div>
    </header>

    <div class="table-wrap">
      <table aria-label="Server deployment records">
        <colgroup>
          <col class="status">
          <col class="service">
          <col class="region">
          <col class="actor">
          <col class="duration">
          <col class="timestamp">
        </colgroup>
        <thead>
          <tr>
            <th scope="col">Status</th>
            <th scope="col">Service name</th>
            <th scope="col">Region</th>
            <th scope="col">Deployed by</th>
            <th scope="col">Duration</th>
            <th scope="col" aria-sort="descending">
              <button class="sort-button" type="button" data-direction="descending" aria-label="Timestamp, sorted newest first">
                Timestamp
                <svg class="sort-icon" viewBox="0 0 16 16" fill="none" aria-hidden="true">
                  <path d="M4 6.25 8 10l4-3.75" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/>
                </svg>
              </button>
            </th>
          </tr>
        </thead>
        <tbody>
          <tr data-time="2025-06-18T14:42:09Z">
            <td>
              <span class="status-pill succeeded">
                <svg class="status-icon" viewBox="0 0 16 16" fill="none" aria-hidden="true">
                  <circle cx="8" cy="8" r="6.25" stroke="currentColor" stroke-width="1.5"/>
                  <path d="m5 8.1 1.8 1.8 4.1-4.1" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
                </svg>
                Succeeded
              </span>
            </td>
            <td>
              <div class="service-cell">
                <span class="service-name">api-gateway</span>
                <span class="revision">a91f2c7</span>
              </div>
            </td>
            <td><span class="region-name">N. Virginia</span><span class="region-code">us-east-1</span></td>
            <td>
              <div class="actor-cell"><span class="avatar">AM</span><span class="actor-name">Avery Morgan</span></div>
            </td>
            <td class="duration">2m 14s</td>
            <td><time datetime="2025-06-18T14:42:09Z">Jun 18, 2025&nbsp; 14:42:09 UTC</time></td>
          </tr>

          <tr class="is-hovered" data-time="2025-06-18T14:31:46Z">
            <td>
              <span class="status-pill deploying">
                <svg class="status-icon" viewBox="0 0 16 16" fill="none" aria-hidden="true">
                  <path d="M8 1.75a6.25 6.25 0 1 1-5.3 2.93" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
                  <path d="M2.15 2.55v2.8h2.8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
                </svg>
                Deploying
              </span>
            </td>
            <td>
              <div class="service-cell">
                <span class="service-name">auth-service</span>
                <span class="revision">f207de1</span>
              </div>
            </td>
            <td><span class="region-name">Frankfurt</span><span class="region-code">eu-central-1</span></td>
            <td>
              <div class="actor-cell"><span class="avatar bot">CI</span><span class="actor-name">release-bot</span></div>
            </td>
            <td class="duration">1m 08s</td>
            <td><time datetime="2025-06-18T14:31:46Z">Jun 18, 2025&nbsp; 14:31:46 UTC</time></td>
          </tr>

          <tr class="is-selected" aria-selected="true" data-time="2025-06-18T13:58:22Z">
            <td>
              <span class="status-pill failed">
                <svg class="status-icon" viewBox="0 0 16 16" fill="none" aria-hidden="true">
                  <circle cx="8" cy="8" r="6.25" stroke="currentColor" stroke-width="1.5"/>
                  <path d="M8 4.5v4.2M8 11.25v.15" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"/>
                </svg>
                Failed
              </span>
            </td>
            <td>
              <div class="service-cell">
                <span class="service-name">billing-worker</span>
                <span class="revision">d84b019</span>
              </div>
            </td>
            <td><span class="region-name">Oregon</span><span class="region-code">us-west-2</span></td>
            <td>
              <div class="actor-cell"><span class="avatar">JL</span><span class="actor-name">Jordan Lee</span></div>
            </td>
            <td class="duration">48s</td>
            <td><time datetime="2025-06-18T13:58:22Z">Jun 18, 2025&nbsp; 13:58:22 UTC</time></td>
          </tr>

          <tr data-time="2025-06-18T13:17:04Z">
            <td>
              <span class="status-pill succeeded">
                <svg class="status-icon" viewBox="0 0 16 16" fill="none" aria-hidden="true">
                  <circle cx="8" cy="8" r="6.25" stroke="currentColor" stroke-width="1.5"/>
                  <path d="m5 8.1 1.8 1.8 4.1-4.1" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
                </svg>
                Succeeded
              </span>
            </td>
            <td>
              <div class="service-cell">
                <span class="service-name">event-processor</span>
                <span class="revision">4c120ab</span>
              </div>
            </td>
            <td><span class="region-name">Singapore</span><span class="region-code">ap-southeast-1</span></td>
            <td>
              <div class="actor-cell"><span class="avatar bot">CI</span><span class="actor-name">release-bot</span></div>
            </td>
            <td class="duration">3m 02s</td>
            <td><time datetime="2025-06-18T13:17:04Z">Jun 18, 2025&nbsp; 13:17:04 UTC</time></td>
          </tr>

          <tr data-time="2025-06-18T12:46:31Z">
            <td>
              <span class="status-pill canceled">
                <svg class="status-icon" viewBox="0 0 16 16" fill="none" aria-hidden="true">
                  <circle cx="8" cy="8" r="6.25" stroke="currentColor" stroke-width="1.5"/>
                  <path d="M5.65 5.65 10.35 10.35M10.35 5.65l-4.7 4.7" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
                </svg>
                Canceled
              </span>
            </td>
            <td>
              <div class="service-cell">
                <span class="service-name">search-indexer</span>
                <span class="revision">09bc73e</span>
              </div>
            </td>
            <td><span class="region-name">London</span><span class="region-code">eu-west-2</span></td>
            <td>
              <div class="actor-cell"><span class="avatar">SP</span><span class="actor-name">Samir Patel</span></div>
            </td>
            <td class="duration">37s</td>
            <td><time datetime="2025-06-18T12:46:31Z">Jun 18, 2025&nbsp; 12:46:31 UTC</time></td>
          </tr>

          <tr data-time="2025-06-18T11:29:18Z">
            <td>
              <span class="status-pill succeeded">
                <svg class="status-icon" viewBox="0 0 16 16" fill="none" aria-hidden="true">
                  <circle cx="8" cy="8" r="6.25" stroke="currentColor" stroke-width="1.5"/>
                  <path d="m5 8.1 1.8 1.8 4.1-4.1" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
                </svg>
                Succeeded
              </span>
            </td>
            <td>
              <div class="service-cell">
                <span class="service-name">notifications</span>
                <span class="revision">ae441cf</span>
              </div>
            </td>
            <td><span class="region-name">Sydney</span><span class="region-code">ap-southeast-2</span></td>
            <td>
              <div class="actor-cell"><span class="avatar">EC</span><span class="actor-name">Elena Cruz</span></div>
            </td>
            <td class="duration">1m 51s</td>
            <td><time datetime="2025-06-18T11:29:18Z">Jun 18, 2025&nbsp; 11:29:18 UTC</time></td>
          </tr>

          <tr data-time="2025-06-18T10:54:07Z">
            <td>
              <span class="status-pill rolled-back">
                <svg class="status-icon" viewBox="0 0 16 16" fill="none" aria-hidden="true">
                  <path d="M5.1 4H2.4V1.3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
                  <path d="M2.65 4.05A6 6 0 1 1 2.2 10" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
                </svg>
                Rolled back
              </span>
            </td>
            <td>
              <div class="service-cell">
                <span class="service-name">media-transformer</span>
                <span class="revision">781d6fa</span>
              </div>
            </td>
            <td><span class="region-name">Tokyo</span><span class="region-code">ap-northeast-1</span></td>
            <td>
              <div class="actor-cell"><span class="avatar bot">OP</span><span class="actor-name">ops-automation</span></div>
            </td>
            <td class="duration">4m 26s</td>
            <td><time datetime="2025-06-18T10:54:07Z">Jun 18, 2025&nbsp; 10:54:07 UTC</time></td>
          </tr>

          <tr data-time="2025-06-18T10:21:53Z">
            <td>
              <span class="status-pill queued">
                <svg class="status-icon" viewBox="0 0 16 16" fill="none" aria-hidden="true">
                  <circle cx="8" cy="8" r="6.25" stroke="currentColor" stroke-width="1.5"/>
                  <path d="M8 4.4v3.9l2.55 1.45" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
                </svg>
                Queued
              </span>
            </td>
            <td>
              <div class="service-cell">
                <span class="service-name">reporting-api</span>
                <span class="revision">c536be8</span>
              </div>
            </td>
            <td><span class="region-name">N. Virginia</span><span class="region-code">us-east-1</span></td>
            <td>
              <div class="actor-cell"><span class="avatar">NK</span><span class="actor-name">Noah Kim</span></div>
            </td>
            <td class="duration">—</td>
            <td><time datetime="2025-06-18T10:21:53Z">Jun 18, 2025&nbsp; 10:21:53 UTC</time></td>
          </tr>
        </tbody>
      </table>
    </div>

    <footer class="table-footer">
      <span>Showing 8 of 8 deployments</span>
      <span class="selection-note">1 row selected <kbd>Esc</kbd> to clear</span>
    </footer>
  </main>

  <script>
    (() => {
      const tbody = document.querySelector("tbody");
      const sortButton = document.querySelector(".sort-button");
      const sortHeader = sortButton.closest("th");

      sortButton.addEventListener("click", () => {
        const nextDirection =
          sortButton.dataset.direction === "descending" ? "ascending" : "descending";
        const rows = Array.from(tbody.querySelectorAll("tr"));

        rows.sort((a, b) => {
          const aTime = Date.parse(a.dataset.time);
          const bTime = Date.parse(b.dataset.time);
          return nextDirection === "ascending" ? aTime - bTime : bTime - aTime;
        });

        rows.forEach(row => tbody.appendChild(row));
        sortButton.dataset.direction = nextDirection;
        sortHeader.setAttribute("aria-sort", nextDirection);
        sortButton.setAttribute(
          "aria-label",
          `Timestamp, sorted ${nextDirection === "ascending" ? "oldest" : "newest"} first`
        );
      });

      tbody.addEventListener("click", event => {
        const row = event.target.closest("tr");
        if (!row) return;

        tbody.querySelectorAll("tr").forEach(item => {
          item.classList.remove("is-selected");
          item.removeAttribute("aria-selected");
        });

        row.classList.add("is-selected");
        row.setAttribute("aria-selected", "true");
      });
    })();
  </script>
</body>
</html>

request envelope — prompt redacted

{
  "model": "openai/gpt-5.6-sol",
  "system": "Respond with a single complete, self-contained HTML document rendering the component and nothing else. No markdown fences, no explanation. Inline all CSS and JavaScript, reference no external resource. Render the component at its natural size on a neutral background, with enough surrounding space that it can be photographed.",
  "messages": [
    {
      "role": "user",
      "content": "«REDACTED — 87 words, 559 chars»"
    }
  ],
  "maxOutputTokens": 32000,
  "gatewayStringId": "90a83792-ef53-41af-8207-229c287092e6"
}