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">
  <title>Server Deployments</title>
  <style>
    :root {
      color-scheme: light;
      --canvas: #f3f4f6;
      --surface: #ffffff;
      --surface-muted: #f8f9fb;
      --border: #dfe3e8;
      --border-soft: #eceff2;
      --text: #17202a;
      --muted: #68717d;
      --faint: #8a939f;
      --blue: #2463a9;
      --blue-bg: #edf5ff;
      --hover-bg: #f5f7f9;
      --green: #26734d;
      --green-bg: #edf8f2;
      --green-border: #cce7d8;
      --red: #a43a3a;
      --red-bg: #fff1f0;
      --red-border: #f0cecb;
      --amber: #8a5c08;
      --amber-bg: #fff7e2;
      --amber-border: #edddb1;
      --gray: #626b76;
      --gray-bg: #f1f3f5;
      --gray-border: #dadddf;
      --purple: #6651a7;
      --purple-bg: #f4f0ff;
      --purple-border: #ddd4f4;
    }

    * {
      box-sizing: border-box;
    }

    body {
      margin: 0;
      min-width: 1080px;
      min-height: 100vh;
      padding: 64px;
      display: flex;
      align-items: flex-start;
      justify-content: center;
      background: var(--canvas);
      color: var(--text);
      font-family: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
      font-size: 13px;
      line-height: 1.4;
    }

    .deployment-panel {
      width: 1152px;
      background: var(--surface);
      border: 1px solid #d8dde3;
      border-radius: 8px;
      box-shadow: 0 1px 2px rgba(20, 29, 38, 0.04);
      overflow: hidden;
    }

    .panel-header {
      height: 68px;
      padding: 0 20px;
      display: flex;
      align-items: center;
      justify-content: space-between;
      border-bottom: 1px solid var(--border);
    }

    .heading-group {
      min-width: 0;
    }

    h1 {
      margin: 0;
      color: #18212b;
      font-size: 16px;
      font-weight: 650;
      letter-spacing: -0.01em;
    }

    .subtitle {
      margin-top: 3px;
      color: var(--muted);
      font-size: 12px;
    }

    .header-tools {
      display: flex;
      align-items: center;
      gap: 8px;
    }

    .record-count {
      margin-right: 6px;
      color: var(--muted);
      font-size: 12px;
      font-variant-numeric: tabular-nums;
    }

    .tool-button {
      height: 30px;
      padding: 0 10px;
      display: inline-flex;
      align-items: center;
      gap: 7px;
      border: 1px solid #d7dce1;
      border-radius: 5px;
      background: #fff;
      color: #3f4852;
      font: inherit;
      font-size: 12px;
      cursor: pointer;
    }

    .tool-button:hover {
      background: #f7f8fa;
      border-color: #cbd1d7;
    }

    .tool-button:focus-visible,
    .sort-button:focus-visible {
      outline: 2px solid rgba(36, 99, 169, 0.35);
      outline-offset: 1px;
    }

    .tool-button svg {
      width: 14px;
      height: 14px;
      stroke: currentColor;
    }

    .table-scroll {
      overflow-x: auto;
    }

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

    caption {
      position: absolute;
      width: 1px;
      height: 1px;
      padding: 0;
      margin: -1px;
      overflow: hidden;
      clip: rect(0, 0, 0, 0);
      white-space: nowrap;
      border: 0;
    }

    col.status-col { width: 142px; }
    col.service-col { width: 221px; }
    col.region-col { width: 164px; }
    col.user-col { width: 178px; }
    col.duration-col { width: 112px; }
    col.time-col { width: 235px; }

    thead {
      background: var(--surface-muted);
    }

    th {
      height: 39px;
      padding: 0 15px;
      border-bottom: 1px solid var(--border);
      color: #626c77;
      font-size: 11px;
      font-weight: 650;
      letter-spacing: 0.025em;
      text-align: left;
      text-transform: uppercase;
      white-space: nowrap;
    }

    th:first-child,
    td:first-child {
      padding-left: 20px;
    }

    th:last-child,
    td:last-child {
      padding-right: 20px;
    }

    .sort-button {
      width: 100%;
      height: 38px;
      margin: 0;
      padding: 0;
      display: inline-flex;
      align-items: center;
      justify-content: flex-start;
      gap: 6px;
      border: 0;
      background: transparent;
      color: inherit;
      font: inherit;
      font-weight: inherit;
      letter-spacing: inherit;
      text-align: left;
      text-transform: inherit;
      cursor: pointer;
    }

    th[aria-sort="ascending"] .sort-button,
    th[aria-sort="descending"] .sort-button {
      color: var(--blue);
    }

    .sort-icon {
      width: 12px;
      height: 12px;
      color: #a1a8b0;
      opacity: 0;
      transition: opacity 120ms ease;
    }

    .sort-button:hover .sort-icon,
    th[aria-sort="ascending"] .sort-icon,
    th[aria-sort="descending"] .sort-icon {
      opacity: 1;
    }

    th[aria-sort="ascending"] .sort-icon {
      transform: rotate(180deg);
      color: var(--blue);
    }

    th[aria-sort="descending"] .sort-icon {
      color: var(--blue);
    }

    tbody tr {
      height: 53px;
      background: #fff;
      cursor: default;
      transition: background-color 90ms ease;
    }

    tbody tr + tr td {
      border-top: 1px solid var(--border-soft);
    }

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

    tbody tr[aria-selected="true"] {
      background: var(--blue-bg);
      box-shadow: inset 3px 0 0 var(--blue);
    }

    tbody tr[aria-selected="true"]:hover {
      background: #e7f1fc;
    }

    td {
      height: 53px;
      padding: 0 15px;
      color: #2f3944;
      vertical-align: middle;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }

    .status {
      width: fit-content;
      min-width: 82px;
      height: 24px;
      padding: 0 8px 0 6px;
      display: inline-flex;
      align-items: center;
      gap: 6px;
      border: 1px solid;
      border-radius: 999px;
      font-size: 11px;
      font-weight: 650;
      line-height: 1;
    }

    .status-symbol {
      width: 14px;
      height: 14px;
      display: inline-flex;
      flex: 0 0 14px;
      align-items: center;
      justify-content: center;
      border-radius: 50%;
      font-size: 10px;
      font-weight: 800;
      line-height: 1;
    }

    .status.success {
      color: var(--green);
      background: var(--green-bg);
      border-color: var(--green-border);
    }

    .success .status-symbol {
      color: #fff;
      background: var(--green);
    }

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

    .failed .status-symbol {
      color: #fff;
      background: var(--red);
    }

    .status.progress {
      color: var(--purple);
      background: var(--purple-bg);
      border-color: var(--purple-border);
    }

    .progress .status-symbol {
      border: 2px solid #c5b9e7;
      border-top-color: var(--purple);
      background: transparent;
    }

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

    .queued .status-symbol {
      color: var(--amber);
      border: 1.5px solid #b98522;
      background: transparent;
      font-size: 8px;
    }

    .status.cancelled {
      color: var(--gray);
      background: var(--gray-bg);
      border-color: var(--gray-border);
    }

    .cancelled .status-symbol {
      color: #fff;
      background: #7a828b;
    }

    .service {
      display: flex;
      align-items: center;
      min-width: 0;
    }

    .service-icon {
      width: 28px;
      height: 28px;
      margin-right: 10px;
      display: inline-flex;
      flex: 0 0 28px;
      align-items: center;
      justify-content: center;
      border: 1px solid #dce1e6;
      border-radius: 5px;
      background: #f7f8fa;
      color: #606b77;
      font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
      font-size: 11px;
      font-weight: 700;
    }

    .service-name {
      min-width: 0;
      overflow: hidden;
      color: #202a35;
      font-weight: 600;
      text-overflow: ellipsis;
    }

    .region {
      display: inline-flex;
      align-items: center;
      gap: 8px;
      font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
      font-size: 12px;
      color: #46515d;
    }

    .region-mark {
      width: 6px;
      height: 6px;
      flex: 0 0 6px;
      border-radius: 50%;
      background: #9ba4ae;
    }

    .user {
      display: flex;
      align-items: center;
      min-width: 0;
      gap: 9px;
    }

    .avatar {
      width: 25px;
      height: 25px;
      display: inline-flex;
      flex: 0 0 25px;
      align-items: center;
      justify-content: center;
      border-radius: 50%;
      background: #e7ebef;
      color: #4d5864;
      font-size: 9px;
      font-weight: 700;
      letter-spacing: 0.02em;
    }

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

    .duration,
    .timestamp {
      font-variant-numeric: tabular-nums;
    }

    .duration {
      color: #48535f;
      font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
      font-size: 12px;
    }

    .timestamp {
      display: flex;
      align-items: baseline;
      gap: 8px;
    }

    .date {
      color: #35404b;
    }

    .time {
      color: var(--muted);
      font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
      font-size: 12px;
    }

    .timezone {
      color: #9098a1;
      font-size: 11px;
    }

    .panel-footer {
      height: 40px;
      padding: 0 20px;
      display: flex;
      align-items: center;
      justify-content: space-between;
      border-top: 1px solid var(--border);
      background: #fbfcfd;
      color: var(--muted);
      font-size: 11px;
    }

    .footer-state {
      display: flex;
      align-items: center;
      gap: 14px;
    }

    .state-key {
      display: inline-flex;
      align-items: center;
      gap: 6px;
    }

    .state-swatch {
      width: 12px;
      height: 12px;
      border: 1px solid #d7dce2;
      border-radius: 2px;
      background: var(--hover-bg);
    }

    .state-swatch.selected {
      border-color: #b7d0eb;
      background: var(--blue-bg);
      box-shadow: inset 2px 0 0 var(--blue);
    }
  </style>
</head>
<body>
  <main class="deployment-panel" aria-labelledby="deployment-title">
    <header class="panel-header">
      <div class="heading-group">
        <h1 id="deployment-title">Server deployments</h1>
        <div class="subtitle">Production and staging activity across all regions</div>
      </div>
      <div class="header-tools">
        <span class="record-count">8 records</span>
        <button class="tool-button" type="button" aria-label="Refresh deployment records">
          <svg viewBox="0 0 24 24" fill="none" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
            <path d="M20 11a8 8 0 1 0-2.34 5.66"></path>
            <path d="M20 5v6h-6"></path>
          </svg>
          Refresh
        </button>
      </div>
    </header>

    <div class="table-scroll">
      <table id="deployment-table">
        <caption>Eight recent server deployment records</caption>
        <colgroup>
          <col class="status-col">
          <col class="service-col">
          <col class="region-col">
          <col class="user-col">
          <col class="duration-col">
          <col class="time-col">
        </colgroup>
        <thead>
          <tr>
            <th scope="col" aria-sort="none">
              <button class="sort-button" type="button" data-key="status">
                Status
                <svg class="sort-icon" viewBox="0 0 12 12" fill="none" aria-hidden="true"><path d="M3 4.5 6 7.5l3-3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
              </button>
            </th>
            <th scope="col" aria-sort="none">
              <button class="sort-button" type="button" data-key="service">
                Service name
                <svg class="sort-icon" viewBox="0 0 12 12" fill="none" aria-hidden="true"><path d="M3 4.5 6 7.5l3-3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
              </button>
            </th>
            <th scope="col" aria-sort="none">
              <button class="sort-button" type="button" data-key="region">
                Region
                <svg class="sort-icon" viewBox="0 0 12 12" fill="none" aria-hidden="true"><path d="M3 4.5 6 7.5l3-3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
              </button>
            </th>
            <th scope="col" aria-sort="none">
              <button class="sort-button" type="button" data-key="user">
                Deployed by
                <svg class="sort-icon" viewBox="0 0 12 12" fill="none" aria-hidden="true"><path d="M3 4.5 6 7.5l3-3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
              </button>
            </th>
            <th scope="col" aria-sort="none">
              <button class="sort-button" type="button" data-key="duration">
                Duration
                <svg class="sort-icon" viewBox="0 0 12 12" fill="none" aria-hidden="true"><path d="M3 4.5 6 7.5l3-3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
              </button>
            </th>
            <th scope="col" aria-sort="descending">
              <button class="sort-button" type="button" data-key="timestamp">
                Timestamp
                <svg class="sort-icon" viewBox="0 0 12 12" fill="none" aria-hidden="true"><path d="M3 4.5 6 7.5l3-3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
              </button>
            </th>
          </tr>
        </thead>
        <tbody>
          <tr data-status="success" data-service="prod-api" data-region="eu-west-1" data-user="Priya Nair" data-duration="134" data-timestamp="20250308144218">
            <td><span class="status success"><span class="status-symbol">✓</span>Success</span></td>
            <td><div class="service"><span class="service-icon">PA</span><span class="service-name">prod-api</span></div></td>
            <td><span class="region"><span class="region-mark"></span>eu-west-1</span></td>
            <td><div class="user"><span class="avatar">PN</span><span class="user-name">Priya Nair</span></div></td>
            <td class="duration">2m 14s</td>
            <td><div class="timestamp"><span class="date">Mar 08, 2025</span><span class="time">14:42:18</span><span class="timezone">UTC</span></div></td>
          </tr>

          <tr aria-selected="true" data-status="failed" data-service="billing-worker" data-region="us-east-1" data-user="Mateo Ruiz" data-duration="48" data-timestamp="20250308143906">
            <td><span class="status failed"><span class="status-symbol">×</span>Failed</span></td>
            <td><div class="service"><span class="service-icon">BW</span><span class="service-name">billing-worker</span></div></td>
            <td><span class="region"><span class="region-mark"></span>us-east-1</span></td>
            <td><div class="user"><span class="avatar">MR</span><span class="user-name">Mateo Ruiz</span></div></td>
            <td class="duration">48s</td>
            <td><div class="timestamp"><span class="date">Mar 08, 2025</span><span class="time">14:39:06</span><span class="timezone">UTC</span></div></td>
          </tr>

          <tr data-status="success" data-service="web-frontend" data-region="ap-southeast-2" data-user="Alex Chen" data-duration="96" data-timestamp="20250308143152">
            <td><span class="status success"><span class="status-symbol">✓</span>Success</span></td>
            <td><div class="service"><span class="service-icon">WF</span><span class="service-name">web-frontend</span></div></td>
            <td><span class="region"><span class="region-mark"></span>ap-southeast-2</span></td>
            <td><div class="user"><span class="avatar">AC</span><span class="user-name">Alex Chen</span></div></td>
            <td class="duration">1m 36s</td>
            <td><div class="timestamp"><span class="date">Mar 08, 2025</span><span class="time">14:31:52</span><span class="timezone">UTC</span></div></td>
          </tr>

          <tr class="is-hovered" data-status="in progress" data-service="auth-service" data-region="eu-central-1" data-user="Sam Okafor" data-duration="73" data-timestamp="20250308142633">
            <td><span class="status progress"><span class="status-symbol"></span>In progress</span></td>
            <td><div class="service"><span class="service-icon">AS</span><span class="service-name">auth-service</span></div></td>
            <td><span class="region"><span class="region-mark"></span>eu-central-1</span></td>
            <td><div class="user"><span class="avatar">SO</span><span class="user-name">Sam Okafor</span></div></td>
            <td class="duration">1m 13s</td>
            <td><div class="timestamp"><span class="date">Mar 08, 2025</span><span class="time">14:26:33</span><span class="timezone">UTC</span></div></td>
          </tr>

          <tr data-status="queued" data-service="notifications" data-region="us-west-2" data-user="Elena Petrova" data-duration="0" data-timestamp="20250308141809">
            <td><span class="status queued"><span class="status-symbol">•</span>Queued</span></td>
            <td><div class="service"><span class="service-icon">NT</span><span class="service-name">notifications</span></div></td>
            <td><span class="region"><span class="region-mark"></span>us-west-2</span></td>
            <td><div class="user"><span class="avatar">EP</span><span class="user-name">Elena Petrova</span></div></td>
            <td class="duration">—</td>
            <td><div class="timestamp"><span class="date">Mar 08, 2025</span><span class="time">14:18:09</span><span class="timezone">UTC</span></div></td>
          </tr>

          <tr data-status="success" data-service="search-indexer" data-region="ca-central-1" data-user="Jordan Lee" data-duration="221" data-timestamp="20250308140641">
            <td><span class="status success"><span class="status-symbol">✓</span>Success</span></td>
            <td><div class="service"><span class="service-icon">SI</span><span class="service-name">search-indexer</span></div></td>
            <td><span class="region"><span class="region-mark"></span>ca-central-1</span></td>
            <td><div class="user"><span class="avatar">JL</span><span class="user-name">Jordan Lee</span></div></td>
            <td class="duration">3m 41s</td>
            <td><div class="timestamp"><span class="date">Mar 08, 2025</span><span class="time">14:06:41</span><span class="timezone">UTC</span></div></td>
          </tr>

          <tr data-status="cancelled" data-service="reporting-api" data-region="eu-west-2" data-user="Nora Singh" data-duration="27" data-timestamp="20250308135417">
            <td><span class="status cancelled"><span class="status-symbol">−</span>Cancelled</span></td>
            <td><div class="service"><span class="service-icon">RA</span><span class="service-name">reporting-api</span></div></td>
            <td><span class="region"><span class="region-mark"></span>eu-west-2</span></td>
            <td><div class="user"><span class="avatar">NS</span><span class="user-name">Nora Singh</span></div></td>
            <td class="duration">27s</td>
            <td><div class="timestamp"><span class="date">Mar 08, 2025</span><span class="time">13:54:17</span><span class="timezone">UTC</span></div></td>
          </tr>

          <tr data-status="success" data-service="edge-proxy" data-region="ap-northeast-1" data-user="Daniel Kim" data-duration="65" data-timestamp="20250308134722">
            <td><span class="status success"><span class="status-symbol">✓</span>Success</span></td>
            <td><div class="service"><span class="service-icon">EP</span><span class="service-name">edge-proxy</span></div></td>
            <td><span class="region"><span class="region-mark"></span>ap-northeast-1</span></td>
            <td><div class="user"><span class="avatar">DK</span><span class="user-name">Daniel Kim</span></div></td>
            <td class="duration">1m 05s</td>
            <td><div class="timestamp"><span class="date">Mar 08, 2025</span><span class="time">13:47:22</span><span class="timezone">UTC</span></div></td>
          </tr>
        </tbody>
      </table>
    </div>

    <footer class="panel-footer">
      <span>Times shown in UTC</span>
      <div class="footer-state" aria-label="Demonstrated row states">
        <span class="state-key"><span class="state-swatch"></span>Hovered row</span>
        <span class="state-key"><span class="state-swatch selected"></span>Selected row</span>
      </div>
    </footer>
  </main>

  <script>
    (() => {
      const table = document.getElementById('deployment-table');
      const tbody = table.querySelector('tbody');
      const headers = [...table.querySelectorAll('th')];
      const buttons = [...table.querySelectorAll('.sort-button')];

      buttons.forEach(button => {
        button.addEventListener('click', () => {
          const activeHeader = button.closest('th');
          const key = button.dataset.key;
          const current = activeHeader.getAttribute('aria-sort');
          const direction = current === 'ascending' ? 'descending' : 'ascending';

          headers.forEach(header => header.setAttribute('aria-sort', 'none'));
          activeHeader.setAttribute('aria-sort', direction);

          const rows = [...tbody.querySelectorAll('tr')];
          rows.sort((a, b) => {
            let first = a.dataset[key];
            let second = b.dataset[key];

            if (key === 'duration' || key === 'timestamp') {
              first = Number(first);
              second = Number(second);
            }

            let result;
            if (typeof first === 'number') {
              result = first - second;
            } else {
              result = first.localeCompare(second, undefined, { numeric: true, sensitivity: 'base' });
            }

            return direction === 'ascending' ? result : -result;
          });

          rows.forEach(row => tbody.appendChild(row));
        });
      });

      tbody.addEventListener('click', event => {
        const row = event.target.closest('tr');
        if (!row) return;
        tbody.querySelectorAll('tr').forEach(item => item.removeAttribute('aria-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"
}