<!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>