<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Server Deployments</title>
<style>
:root {
color-scheme: light;
--bg: #f2f4f7;
--surface: #ffffff;
--line: #e3e7ec;
--line-strong: #d4dbe3;
--text: #202833;
--muted: #697586;
--subtle: #8a95a3;
--blue: #2f6fed;
--blue-soft: #eef4ff;
--blue-line: #9ebbf8;
--green: #2d8659;
--amber: #a56c13;
--red: #b44747;
}
* { box-sizing: border-box; }
body {
margin: 0;
min-width: 980px;
background: var(--bg);
color: var(--text);
font-family: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
font-size: 13px;
line-height: 1.4;
}
.page {
padding: 58px 44px 72px;
}
.table-card {
width: 1080px;
margin: 0 auto;
overflow: hidden;
background: var(--surface);
border: 1px solid var(--line-strong);
border-radius: 8px;
box-shadow: 0 7px 20px rgba(32, 40, 51, .06);
}
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
min-height: 76px;
padding: 19px 24px 18px;
border-bottom: 1px solid var(--line);
}
.eyebrow {
margin: 0 0 3px;
color: var(--muted);
font-size: 11px;
font-weight: 650;
letter-spacing: .08em;
text-transform: uppercase;
}
h1 {
margin: 0;
color: var(--text);
font-size: 18px;
font-weight: 650;
letter-spacing: -.01em;
}
.header-meta {
display: flex;
align-items: center;
gap: 14px;
color: var(--muted);
font-size: 12px;
}
.count {
padding-right: 14px;
border-right: 1px solid var(--line);
}
.sync {
display: inline-flex;
align-items: center;
gap: 6px;
}
.sync-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--green);
}
.table-wrap {
overflow: hidden;
}
table {
width: 100%;
border-collapse: collapse;
table-layout: fixed;
}
col.status { width: 154px; }
col.service { width: 235px; }
col.region { width: 145px; }
col.deployed { width: 170px; }
col.duration { width: 126px; }
col.timestamp { width: 250px; }
th {
height: 43px;
padding: 0 16px;
border-bottom: 1px solid var(--line-strong);
background: #fafbfc;
color: var(--muted);
font-size: 11px;
font-weight: 650;
letter-spacing: .045em;
text-align: left;
text-transform: uppercase;
}
th:first-child, td:first-child { padding-left: 24px; }
th:last-child, td:last-child { padding-right: 24px; }
th button {
display: inline-flex;
align-items: center;
gap: 7px;
padding: 0;
border: 0;
background: transparent;
color: inherit;
font: inherit;
letter-spacing: inherit;
text-align: left;
text-transform: inherit;
cursor: pointer;
}
th button:hover { color: var(--text); }
th[aria-sort="ascending"] button,
th[aria-sort="descending"] button {
color: var(--text);
}
.sort-icon {
position: relative;
display: inline-flex;
width: 12px;
height: 14px;
align-items: center;
justify-content: center;
color: var(--blue);
font-size: 15px;
font-weight: 700;
line-height: 1;
}
.sort-icon::after {
content: "↑";
}
th[aria-sort="descending"] .sort-icon::after {
content: "↓";
}
td {
height: 61px;
padding: 0 16px;
border-bottom: 1px solid var(--line);
color: var(--text);
vertical-align: middle;
white-space: nowrap;
}
tbody tr:last-child td { border-bottom: 0; }
tbody tr {
transition: background .12s ease, box-shadow .12s ease;
}
tbody tr:hover,
tbody tr.is-hovered {
background: #f5f8fd;
box-shadow: inset 3px 0 0 #8eafea;
}
tbody tr.is-selected {
background: var(--blue-soft);
box-shadow: inset 3px 0 0 var(--blue);
}
tbody tr.is-selected:hover,
tbody tr.is-selected.is-hovered {
background: #eaf2ff;
}
.status-cell {
display: flex;
align-items: center;
gap: 9px;
font-weight: 600;
}
.status-mark {
position: relative;
display: inline-flex;
width: 18px;
height: 18px;
align-items: center;
justify-content: center;
border: 1px solid currentColor;
border-radius: 50%;
font-size: 11px;
font-weight: 750;
}
.status-mark::before { content: "✓"; }
.status-running { color: var(--green); }
.status-running .status-mark { background: #edf8f1; }
.status-pending { color: var(--amber); }
.status-pending .status-mark { background: #fff8e9; }
.status-pending .status-mark::before { content: "…"; margin-top: -3px; }
.status-failed { color: var(--red); }
.status-failed .status-mark { background: #fff0f0; }
.status-failed .status-mark::before { content: "!"; }
.service-name {
display: block;
color: var(--text);
font-weight: 620;
}
.service-id {
display: block;
margin-top: 2px;
color: var(--subtle);
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: 11px;
}
.region-name, .deployer { color: #394554; }
.region-code {
display: block;
margin-top: 2px;
color: var(--subtle);
font-size: 11px;
}
.duration {
color: #465363;
font-variant-numeric: tabular-nums;
}
.timestamp {
color: #4b5868;
font-variant-numeric: tabular-nums;
}
.selected-box {
width: 15px;
height: 15px;
margin: 0 10px 0 0;
vertical-align: -3px;
accent-color: var(--blue);
cursor: pointer;
}
.table-footer {
display: flex;
align-items: center;
justify-content: space-between;
min-height: 43px;
padding: 0 24px;
border-top: 1px solid var(--line);
background: #fcfdfe;
color: var(--muted);
font-size: 12px;
}
.selection-note {
display: inline-flex;
align-items: center;
gap: 7px;
}
.selection-chip {
width: 8px;
height: 8px;
border-radius: 2px;
background: var(--blue);
}
.sort-note {
color: var(--subtle);
}
</style>
</head>
<body>
<main class="page">
<section class="table-card" aria-labelledby="deployments-title">
<header class="card-header">
<div>
<p class="eyebrow">Infrastructure / Deployments</p>
<h1 id="deployments-title">Server deployment records</h1>
</div>
<div class="header-meta">
<span class="count">8 records</span>
<span class="sync"><span class="sync-dot" aria-hidden="true"></span>Updated 2 min ago</span>
</div>
</header>
<div class="table-wrap">
<table aria-describedby="table-summary">
<colgroup>
<col class="status">
<col class="service">
<col class="region">
<col class="deployed">
<col class="duration">
<col class="timestamp">
</colgroup>
<thead>
<tr>
<th scope="col"><button type="button" data-key="status">Status</button></th>
<th scope="col"><button type="button" data-key="service">Service name</button></th>
<th scope="col"><button type="button" data-key="region">Region</button></th>
<th scope="col"><button type="button" data-key="deployed">Deployed by</button></th>
<th scope="col"><button type="button" data-key="duration">Duration</button></th>
<th scope="col" aria-sort="ascending">
<button type="button" data-key="timestamp">Timestamp <span class="sort-icon" aria-hidden="true"></span></button>
</th>
</tr>
</thead>
<tbody id="deployment-rows">
<tr data-timestamp="2025-04-18T08:42:00Z">
<td><span class="status-cell status-running"><span class="status-mark" aria-hidden="true"></span>Running</span></td>
<td><span class="service-name">api-gateway</span><span class="service-id">v2.18.4 · 8f31c2a</span></td>
<td><span class="region-name">US East</span><span class="region-code">us-east-1</span></td>
<td class="deployer">Maya Chen</td>
<td class="duration">4m 12s</td>
<td class="timestamp">Apr 18, 2025 · 08:42 UTC</td>
</tr>
<tr data-timestamp="2025-04-18T08:35:00Z">
<td><span class="status-cell status-running"><span class="status-mark" aria-hidden="true"></span>Running</span></td>
<td><span class="service-name">billing-worker</span><span class="service-id">v4.6.1 · 1b7d9e0</span></td>
<td><span class="region-name">EU Central</span><span class="region-code">eu-central-1</span></td>
<td class="deployer">Jon Bell</td>
<td class="duration">2m 48s</td>
<td class="timestamp">Apr 18, 2025 · 08:35 UTC</td>
</tr>
<tr class="is-selected" data-timestamp="2025-04-18T08:21:00Z" aria-selected="true">
<td><input class="selected-box" type="checkbox" checked aria-label="Select search-indexer"><span class="status-cell status-running"><span class="status-mark" aria-hidden="true"></span>Running</span></td>
<td><span class="service-name">search-indexer</span><span class="service-id">v7.3.0 · 6c20a4f</span></td>
<td><span class="region-name">US West</span><span class="region-code">us-west-2</span></td>
<td class="deployer">Ari Patel</td>
<td class="duration">6m 03s</td>
<td class="timestamp">Apr 18, 2025 · 08:21 UTC</td>
</tr>
<tr data-timestamp="2025-04-18T08:04:00Z">
<td><span class="status-cell status-pending"><span class="status-mark" aria-hidden="true"></span>Pending</span></td>
<td><span class="service-name">notifications</span><span class="service-id">v3.11.2 · d9a6e12</span></td>
<td><span class="region-name">Asia Pacific</span><span class="region-code">ap-southeast-1</span></td>
<td class="deployer">Lena Ortiz</td>
<td class="duration">—</td>
<td class="timestamp">Apr 18, 2025 · 08:04 UTC</td>
</tr>
<tr data-timestamp="2025-04-18T07:51:00Z">
<td><span class="status-cell status-running"><span class="status-mark" aria-hidden="true"></span>Running</span></td>
<td><span class="service-name">media-transcoder</span><span class="service-id">v1.9.8 · e31f0b6</span></td>
<td><span class="region-name">EU West</span><span class="region-code">eu-west-1</span></td>
<td class="deployer">Maya Chen</td>
<td class="duration">3m 37s</td>
<td class="timestamp">Apr 18, 2025 · 07:51 UTC</td>
</tr>
<tr class="is-hovered" data-timestamp="2025-04-18T07:38:00Z" aria-label="Hovered row preview">
<td><span class="status-cell status-failed"><span class="status-mark" aria-hidden="true"></span>Failed</span></td>
<td><span class="service-name">checkout-api</span><span class="service-id">v5.0.3 · 4a81c7d</span></td>
<td><span class="region-name">US East</span><span class="region-code">us-east-1</span></td>
<td class="deployer">Sam Rivera</td>
<td class="duration">1m 19s</td>
<td class="timestamp">Apr 18, 2025 · 07:38 UTC</td>
</tr>
<tr data-timestamp="2025-04-18T07:16:00Z">
<td><span class="status-cell status-running"><span class="status-mark" aria-hidden="true"></span>Running</span></td>
<td><span class="service-name">fraud-detector</span><span class="service-id">v2.4.7 · 0fc2d81</span></td>
<td><span class="region-name">US Central</span><span class="region-code">us-central-1</span></td>
<td class="deployer">Jon Bell</td>
<td class="duration">5m 26s</td>
<td class="timestamp">Apr 18, 2025 · 07:16 UTC</td>
</tr>
<tr data-timestamp="2025-04-18T06:59:00Z">
<td><span class="status-cell status-running"><span class="status-mark" aria-hidden="true"></span>Running</span></td>
<td><span class="service-name">config-service</span><span class="service-id">v6.2.5 · a5e901b</span></td>
<td><span class="region-name">EU Central</span><span class="region-code">eu-central-1</span></td>
<td class="deployer">Ari Patel</td>
<td class="duration">2m 06s</td>
<td class="timestamp">Apr 18, 2025 · 06:59 UTC</td>
</tr>
</tbody>
</table>
</div>
<footer class="table-footer" id="table-summary">
<span class="selection-note"><span class="selection-chip" aria-hidden="true"></span>1 deployment selected</span>
<span class="sort-note">Timestamp · newest first</span>
</footer>
</section>
</main>
<script>
const table = document.querySelector("table");
const tbody = document.getElementById("deployment-rows");
const headers = [...table.querySelectorAll("th")];
headers.forEach((header, index) => {
const button = header.querySelector("button");
button.addEventListener("click", () => {
const key = button.dataset.key;
const current = header.getAttribute("aria-sort");
const direction = current === "ascending" ? "descending" : "ascending";
headers.forEach(h => h.removeAttribute("aria-sort"));
header.setAttribute("aria-sort", direction);
const rows = [...tbody.querySelectorAll("tr")];
rows.sort((a, b) => {
let av, bv;
if (key === "timestamp") {
av = new Date(a.dataset.timestamp).getTime();
bv = new Date(b.dataset.timestamp).getTime();
} else {
av = a.cells[index].innerText.trim().toLowerCase();
bv = b.cells[index].innerText.trim().toLowerCase();
}
if (av < bv) return direction === "ascending" ? -1 : 1;
if (av > bv) return direction === "ascending" ? 1 : -1;
return 0;
});
rows.forEach(row => tbody.appendChild(row));
});
});
tbody.addEventListener("change", event => {
if (!event.target.classList.contains("selected-box")) return;
const row = event.target.closest("tr");
row.classList.toggle("is-selected", event.target.checked);
row.setAttribute("aria-selected", event.target.checked ? "true" : "false");
});
</script>
</body>
</html>