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">
<title>Deployments — data table</title>
<style>
  :root{
    --bg:#eceae6;
    --card:#ffffff;
    --line:#e4e2de;
    --line-soft:#f0efec;
    --ink:#23211e;
    --ink-2:#55524d;
    --ink-3:#8a867f;
    --accent:#33608f;
    --sel:#eef3f9;
    --hov:#f6f5f2;
  }
  *{box-sizing:border-box}
  html,body{height:100%}
  body{
    margin:0;
    background:var(--bg);
    display:flex;
    align-items:center;
    justify-content:center;
    padding:72px 56px;
    font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Helvetica Neue",Arial,sans-serif;
    color:var(--ink);
    -webkit-font-smoothing:antialiased;
  }

  .panel{
    width:940px;
    background:var(--card);
    border:1px solid var(--line);
    border-radius:6px;
    box-shadow:0 1px 1px rgba(30,28,25,.03), 0 8px 24px -12px rgba(30,28,25,.14);
    overflow:hidden;
  }

  /* ---- panel head ---- */
  .head{
    display:flex;
    align-items:baseline;
    gap:10px;
    padding:14px 16px 13px;
    border-bottom:1px solid var(--line);
  }
  .head h1{
    margin:0;
    font-size:13.5px;
    font-weight:650;
    letter-spacing:.005em;
  }
  .head .count{font-size:12px;color:var(--ink-3)}
  .head .spacer{flex:1}
  .head .selnote{
    font-size:11.5px;
    color:var(--accent);
    background:var(--sel);
    border:1px solid #d5e2ef;
    border-radius:3px;
    padding:2px 7px;
  }

  /* ---- table ---- */
  table{
    width:100%;
    border-collapse:separate;
    border-spacing:0;
    font-size:12.5px;
    line-height:1.35;
  }
  thead th{
    text-align:left;
    font-size:10.5px;
    font-weight:650;
    letter-spacing:.075em;
    text-transform:uppercase;
    color:var(--ink-3);
    background:#fbfaf9;
    padding:0;
    border-bottom:1px solid var(--line);
    white-space:nowrap;
    user-select:none;
  }
  thead th .lbl{display:block;padding:8px 12px}
  thead th.num .lbl{text-align:right}

  thead th.sortable button{
    all:unset;
    display:flex;
    align-items:center;
    gap:5px;
    width:100%;
    padding:8px 12px;
    font:inherit;
    letter-spacing:inherit;
    text-transform:inherit;
    color:inherit;
    cursor:pointer;
  }
  thead th.sortable button:hover{color:var(--ink-2);background:#f4f2ef}
  thead th[aria-sort]{
    color:var(--ink);
    background:#f4f2ef;
    box-shadow:inset 0 -2px 0 var(--accent);
  }
  thead th[aria-sort] button{color:var(--ink)}
  .caret{
    font-size:8px;
    line-height:1;
    color:var(--accent);
    transform:translateY(.5px);
  }
  thead th.sortable:not([aria-sort]) .caret{color:#c6c2bb}

  tbody td{
    padding:0 12px;
    height:40px;
    border-bottom:1px solid var(--line-soft);
    color:var(--ink-2);
    vertical-align:middle;
    background:#fff;
  }
  tbody tr:last-child td{border-bottom:0}
  tbody td.name{color:var(--ink);font-weight:550}
  .mono{
    font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace;
    font-size:11.5px;
    font-variant-numeric:tabular-nums;
    letter-spacing:-.01em;
  }
  td.num{text-align:right}
  .ts .rel{color:var(--ink-3);margin-left:8px;font-size:11px}

  /* selection gutter */
  th.gut,td.gut{width:34px;padding-left:14px;padding-right:0;position:relative}
  .box{
    width:13px;height:13px;
    border:1px solid #bdb9b2;
    border-radius:3px;
    background:#fff;
    display:block;
    position:relative;
  }
  .box.on{background:var(--accent);border-color:var(--accent)}
  .box.on::after{
    content:"";
    position:absolute;left:3px;top:1.5px;
    width:5px;height:8px;
    border:1.6px solid #fff;
    border-top:0;border-left:0;
    transform:rotate(40deg);
  }
  th.gut .box{border-color:#cdc9c2}

  /* row states */
  tbody tr.hovered td{background:var(--hov)}
  tbody tr.selected td{background:var(--sel);border-bottom-color:#dfe8f1}
  tbody tr.selected td.name{color:#1c2b3a}
  tbody tr.selected td.gut::before{
    content:"";
    position:absolute;left:0;top:0;bottom:0;
    width:2px;background:var(--accent);
  }
  tbody tr:hover td{background:var(--hov)}
  tbody tr.selected:hover td{background:#e7eef7}

  /* ---- status ---- */
  .st{
    display:inline-flex;
    align-items:center;
    gap:6px;
    padding:2px 8px 2px 5px;
    border:1px solid;
    border-radius:11px;
    font-size:11px;
    font-weight:650;
    letter-spacing:.01em;
    white-space:nowrap;
  }
  .st .g{
    width:13px;height:13px;
    border-radius:50%;
    display:inline-grid;
    place-items:center;
    font-size:8.5px;
    font-weight:700;
    line-height:1;
    color:#fff;
    flex:0 0 auto;
  }
  .ok   {color:#1c5c3c;background:#edf5f0;border-color:#c9e0d3}
  .ok .g{background:#2b7a52}
  .bad  {color:#8f281d;background:#fbeeec;border-color:#eed2cd}
  .bad .g{background:#b5382b}
  .run  {color:#1f4f7a;background:#eaf1f8;border-color:#cddeed}
  .run .g{background:#2f6ea3}
  .back {color:#7a5312;background:#fbf3e4;border-color:#ecdcbb}
  .back .g{background:#9a6b16}
  .queue{color:#57534c;background:#f4f3f1;border-color:#dcd9d3;border-style:dashed}
  .queue .g{background:#8a867f}

  /* ---- foot ---- */
  .foot{
    display:flex;
    align-items:center;
    gap:14px;
    padding:10px 16px;
    border-top:1px solid var(--line);
    background:#fbfaf9;
    font-size:11.5px;
    color:var(--ink-3);
  }
  .foot .spacer{flex:1}
  .legend{display:flex;gap:12px;align-items:center}
  .legend span{display:inline-flex;align-items:center;gap:4px}
  .legend i{
    width:11px;height:11px;border-radius:50%;
    display:inline-grid;place-items:center;
    font-size:7.5px;font-weight:700;font-style:normal;color:#fff;
  }
  .pager{display:flex;gap:4px}
  .pager b{
    font-weight:550;color:var(--ink-2);
    border:1px solid var(--line);background:#fff;
    border-radius:3px;padding:2px 7px;
  }
  .pager b.off{color:#b6b2ab}
</style>
</head>
<body>

<div class="panel" role="region" aria-label="Server deployment records">
  <div class="head">
    <h1>Deployments</h1>
    <span class="count">8 of 124</span>
    <span class="spacer"></span>
    <span class="selnote">1 row selected</span>
  </div>

  <table>
    <colgroup>
      <col style="width:34px"><col style="width:132px"><col style="width:180px">
      <col style="width:118px"><col style="width:132px"><col style="width:92px"><col>
    </colgroup>
    <thead>
      <tr>
        <th class="gut" scope="col"><span class="box" aria-hidden="true"></span><span class="sr" style="position:absolute;left:-9999px">Select</span></th>
        <th class="sortable" scope="col"><button type="button">Status <span class="caret" aria-hidden="true">▾</span></button></th>
        <th class="sortable" scope="col"><button type="button">Service <span class="caret" aria-hidden="true">▾</span></button></th>
        <th class="sortable" scope="col"><button type="button">Region <span class="caret" aria-hidden="true">▾</span></button></th>
        <th scope="col"><span class="lbl">Deployed by</span></th>
        <th class="sortable num" scope="col" style="text-align:right"><button type="button" style="justify-content:flex-end">Duration <span class="caret" aria-hidden="true">▾</span></button></th>
        <th class="sortable" scope="col" aria-sort="descending"><button type="button">Timestamp <span class="caret" aria-hidden="true">▼</span></button></th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td class="gut"><span class="box" aria-hidden="true"></span></td>
        <td><span class="st ok"><span class="g" aria-hidden="true">✓</span>Succeeded</span></td>
        <td class="name">api-gateway</td>
        <td class="mono">us-east-1</td>
        <td>m.okafor</td>
        <td class="num mono">2m 14s</td>
        <td class="ts mono">2024-03-18 14:32:07 <span class="rel">4m ago</span></td>
      </tr>

      <tr>
        <td class="gut"><span class="box" aria-hidden="true"></span></td>
        <td><span class="st run"><span class="g" aria-hidden="true">▶</span>Running</span></td>
        <td class="name">search-indexer</td>
        <td class="mono">ap-south-1</td>
        <td>r.almeida</td>
        <td class="num mono">3m 27s</td>
        <td class="ts mono">2024-03-18 14:28:55 <span class="rel">7m ago</span></td>
      </tr>

      <tr class="hovered">
        <td class="gut"><span class="box" aria-hidden="true"></span></td>
        <td><span class="st bad"><span class="g" aria-hidden="true">✕</span>Failed</span></td>
        <td class="name">payments-core</td>
        <td class="mono">us-east-1</td>
        <td>l.tanaka</td>
        <td class="num mono">0m 48s</td>
        <td class="ts mono">2024-03-18 14:11:02 <span class="rel">25m ago</span></td>
      </tr>

      <tr class="selected" aria-selected="true">
        <td class="gut"><span class="box on" aria-hidden="true"></span></td>
        <td><span class="st back"><span class="g" aria-hidden="true">↺</span>Rolled back</span></td>
        <td class="name">notifications-api</td>
        <td class="mono">eu-central-1</td>
        <td>ci-bot / #4471</td>
        <td class="num mono">5m 11s</td>
        <td class="ts mono">2024-03-18 13:56:40 <span class="rel">39m ago</span></td>
      </tr>

      <tr>
        <td class="gut"><span class="box" aria-hidden="true"></span></td>
        <td><span class="st ok"><span class="g" aria-hidden="true">✓</span>Succeeded</span></td>
        <td class="name">checkout-web</td>
        <td class="mono">eu-west-1</td>
        <td>ci-bot / #4470</td>
        <td class="num mono">1m 02s</td>
        <td class="ts mono">2024-03-18 13:40:19 <span class="rel">56m ago</span></td>
      </tr>

      <tr>
        <td class="gut"><span class="box" aria-hidden="true"></span></td>
        <td><span class="st queue"><span class="g" aria-hidden="true">·</span>Queued</span></td>
        <td class="name">media-transcoder</td>
        <td class="mono">sa-east-1</td>
        <td>d.novak</td>
        <td class="num mono">—</td>
        <td class="ts mono">2024-03-18 13:22:04 <span class="rel">1h ago</span></td>
      </tr>

      <tr>
        <td class="gut"><span class="box" aria-hidden="true"></span></td>
        <td><span class="st ok"><span class="g" aria-hidden="true">✓</span>Succeeded</span></td>
        <td class="name">auth-service</td>
        <td class="mono">us-west-2</td>
        <td>s.mbeki</td>
        <td class="num mono">4m 38s</td>
        <td class="ts mono">2024-03-18 12:58:31 <span class="rel">2h ago</span></td>
      </tr>

      <tr>
        <td class="gut"><span class="box" aria-hidden="true"></span></td>
        <td><span class="st ok"><span class="g" aria-hidden="true">✓</span>Succeeded</span></td>
        <td class="name">billing-cron</td>
        <td class="mono">us-east-1</td>
        <td>ci-bot / #4468</td>
        <td class="num mono">0m 26s</td>
        <td class="ts mono">2024-03-18 12:30:00 <span class="rel">2h ago</span></td>
      </tr>
    </tbody>
  </table>

  <div class="foot">
    <div class="legend" aria-hidden="true">
      <span><i style="background:#2b7a52">✓</i>ok</span>
      <span><i style="background:#b5382b">✕</i>failed</span>
      <span><i style="background:#2f6ea3">▶</i>running</span>
      <span><i style="background:#9a6b16">↺</i>rolled back</span>
      <span><i style="background:#8a867f">·</i>queued</span>
    </div>
    <span class="spacer"></span>
    <span>Sorted by timestamp, newest first</span>
    <div class="pager"><b class="off">Prev</b><b>Next</b></div>
  </div>
</div>

<script>
  // Minimal, local-only sort affordance: clicking a header cycles its indicator.
  document.querySelectorAll('thead th.sortable button').forEach(function (btn) {
    btn.addEventListener('click', function () {
      var th = btn.closest('th');
      var was = th.getAttribute('aria-sort');
      document.querySelectorAll('thead th.sortable').forEach(function (h) {
        h.removeAttribute('aria-sort');
        h.querySelector('.caret').textContent = '▾';
      });
      var next = was === 'descending' ? 'ascending' : 'descending';
      th.setAttribute('aria-sort', next);
      th.querySelector('.caret').textContent = next === 'descending' ? '▼' : '▲';
    });
  });
</script>
</body>
</html>

request envelope — prompt redacted

{
  "model": "anthropic/claude-opus-5",
  "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": "e1ccbf59-1f3c-41f8-b074-88ee6a731c02"
}