Extract inline styles into CSS classes in templates
All style attributes replaced with semantic class names defined in _css.html. Makes the templates readable and keeps styling centralized for future pages.
diff --git a/src/quire/web.rs b/src/quire/web.rs
index 125bd82..43724ad 100644
--- a/src/quire/web.rs
+++ b/src/quire/web.rs
@@ -141,9 +141,9 @@ impl<S: Send + Sync> FromRequestParts<S> for RemoteUser {
fn state_color(state: &str) -> &'static str {
match state {
- "complete" => "var(--ok)",
- "failed" => "var(--bad)",
- _ => "var(--muted)",
+ "complete" => "c-ok",
+ "failed" => "c-bad",
+ _ => "c-muted",
}
}
diff --git a/templates/_base.html b/templates/_base.html
index c4534cc..fa8081c 100644
--- a/templates/_base.html
+++ b/templates/_base.html
@@ -8,7 +8,7 @@
</head>
<body>
{% block nav %}{% endblock %}
-<main style="padding:22px 56px 32px">
+<main class="page-main">
{% block content %}{% endblock %}
</main>
{% include "_footer.html" %}
diff --git a/templates/_css.html b/templates/_css.html
index b2e65ee..fa8d6c4 100644
--- a/templates/_css.html
+++ b/templates/_css.html
@@ -12,6 +12,177 @@
--ok: #4a7a3a;
--bad: #9a3a28;
}
-body { margin:0; background:var(--bg); color:var(--ink); font-family:var(--font-humanist); font-size:15px; line-height:1.6; }
-a { color:var(--accent); }
-pre { white-space:pre-wrap; word-break:break-word; }
+
+body {
+ margin: 0;
+ background: var(--bg);
+ color: var(--ink);
+ font-family: var(--font-humanist);
+ font-size: 15px;
+ line-height: 1.6;
+}
+
+a { color: var(--accent); }
+pre { white-space: pre-wrap; word-break: break-word; }
+
+/* Layout */
+
+.page-nav {
+ padding: 14px 56px;
+ border-bottom: 1px solid var(--rule);
+ font-family: var(--font-mono);
+ font-size: 14px;
+ font-weight: 500;
+ letter-spacing: -0.2px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.page-nav .breadcrumbs .sep { color: var(--rule2); }
+.page-nav .crumb-muted { color: var(--muted); }
+.page-nav .crumb-faint { color: var(--mutedFaint); }
+.page-nav .shortcuts { font-size: 11px; color: var(--mutedFaint); }
+
+.page-main { padding: 22px 56px 32px; }
+
+.page-footer {
+ padding: 16px 56px 24px;
+ border-top: 1px solid var(--rule);
+ font-family: var(--font-mono);
+ font-size: 11px;
+ color: var(--mutedFaint);
+ letter-spacing: 0.2px;
+ display: flex;
+ justify-content: space-between;
+}
+
+/* CI run list */
+
+.ci-heading {
+ font-family: var(--font-mono);
+ font-size: 19px;
+ font-weight: 600;
+ margin: 0 0 16px;
+}
+
+.ci-table {
+ width: 100%;
+ border-collapse: collapse;
+ font-family: var(--font-mono);
+ font-size: 12.5px;
+ line-height: 1.6;
+}
+
+.ci-table thead th {
+ text-align: left;
+ padding: 6px 8px;
+ font-weight: 400;
+ color: var(--mutedFaint);
+}
+
+.ci-table thead tr { border-bottom: 1px solid var(--rule2); }
+.ci-table tbody { border-top: 1px solid var(--rule); }
+.ci-table td { padding: 6px 8px; }
+.ci-table .empty { padding: 16px; color: var(--muted); }
+
+.ci-status-dot {
+ display: inline-block;
+ width: 6px;
+ height: 6px;
+ border-radius: 3px;
+}
+
+.ci-sha-link {
+ color: var(--accent);
+ text-decoration: none;
+ border-bottom: 1px dotted var(--rule2);
+}
+
+/* CI run detail */
+
+.ci-meta {
+ padding: 16px 0;
+ border-bottom: 1px solid var(--rule);
+}
+
+.ci-meta-primary {
+ font-family: var(--font-mono);
+ font-size: 15px;
+ line-height: 1.6;
+}
+
+.ci-meta-secondary {
+ font-family: var(--font-mono);
+ font-size: 12px;
+ color: var(--mutedFaint);
+ margin-top: 4px;
+}
+
+/* CI jobs */
+
+.ci-job {
+ margin: 24px 0 0;
+}
+
+.ci-job-header {
+ font-family: var(--font-mono);
+ font-size: 13px;
+ font-weight: 500;
+ padding: 8px 0;
+ border-bottom: 1px solid var(--rule2);
+}
+
+.ci-sh {
+ margin: 8px 0;
+}
+
+.ci-sh-meta {
+ font-family: var(--font-mono);
+ font-size: 11px;
+ color: var(--mutedFaint);
+ margin-bottom: 2px;
+}
+
+.ci-sh-cmd {
+ font-family: var(--font-mono);
+ font-size: 12px;
+ color: var(--muted);
+ margin-bottom: 4px;
+}
+
+.ci-sh-log {
+ font-family: var(--font-mono);
+ font-size: 12px;
+ line-height: 1.65;
+ background: var(--code);
+ color: var(--ink);
+ padding: 10px 14px;
+ border-left: 2px solid var(--accent);
+ overflow: auto;
+ margin: 0 0 8px;
+}
+
+.ci-empty { padding: 16px 0; color: var(--muted); }
+
+/* Error page */
+
+.error-title {
+ color: var(--bad);
+ font-family: var(--font-mono);
+}
+
+.error-detail {
+ font-family: var(--font-mono);
+ font-size: 12px;
+ background: var(--code);
+ padding: 14px 18px;
+ overflow: auto;
+}
+
+/* Color tokens */
+
+.c-ok { color: var(--ok); }
+.c-bad { color: var(--bad); }
+.c-muted { color: var(--muted); }
+.c-accent { color: var(--accent); }
diff --git a/templates/_footer.html b/templates/_footer.html
index e4ad42d..0bb35d0 100644
--- a/templates/_footer.html
+++ b/templates/_footer.html
@@ -1,4 +1,4 @@
-<footer style="padding:16px 56px 24px;border-top:1px solid var(--rule);font-family:var(--font-mono);font-size:11px;color:var(--mutedFaint);letter-spacing:0.2px;display:flex;justify-content:space-between">
-<span>quire</span>
-<span>?</span>
+<footer class="page-footer">
+ <span>quire</span>
+ <span>?</span>
</footer>
diff --git a/templates/_nav.html b/templates/_nav.html
index d89ca16..b38391b 100644
--- a/templates/_nav.html
+++ b/templates/_nav.html
@@ -1,4 +1,10 @@
-<nav style="padding:14px 56px;border-bottom:1px solid var(--rule);font-family:var(--font-mono);font-size:14px;font-weight:500;letter-spacing:-0.2px;display:flex;justify-content:space-between;align-items:center">
-<div><span style="color:var(--mutedFaint)">quire</span> <span style="color:var(--rule2)">/</span> <span>{{ repo }}</span> <span style="color:var(--rule2)">/</span> <span style="color:var(--muted)">{{ page }}</span></div>
-<div style="font-size:11px;color:var(--mutedFaint)">press [?] for shortcuts</div>
+<nav class="page-nav">
+ <div class="breadcrumbs">
+ <span class="crumb-faint">quire</span>
+ <span class="sep">/</span>
+ <span>{{ repo }}</span>
+ <span class="sep">/</span>
+ <span class="crumb-muted">{{ page }}</span>
+ </div>
+ <div class="shortcuts">press [?] for shortcuts</div>
</nav>
diff --git a/templates/ci/run_detail.html b/templates/ci/run_detail.html
index 6841148..f43e154 100644
--- a/templates/ci/run_detail.html
+++ b/templates/ci/run_detail.html
@@ -3,41 +3,49 @@
{% block title %}ci · {{ repo }} · {{ run.sha_short }}{% endblock %}
{% block nav %}
-<nav style="padding:14px 56px;border-bottom:1px solid var(--rule);font-family:var(--font-mono);font-size:14px;font-weight:500;letter-spacing:-0.2px;display:flex;justify-content:space-between;align-items:center">
-<div><span style="color:var(--mutedFaint)">quire</span> <span style="color:var(--rule2)">/</span> <span>{{ repo }}</span> <span style="color:var(--rule2)">/</span> <span style="color:var(--muted)">ci · {{ run.sha_short }}</span></div>
-<div style="font-size:11px;color:var(--mutedFaint)">press [?] for shortcuts</div>
+<nav class="page-nav">
+ <div class="breadcrumbs">
+ <span class="crumb-faint">quire</span>
+ <span class="sep">/</span>
+ <span>{{ repo }}</span>
+ <span class="sep">/</span>
+ <span class="crumb-muted">ci · {{ run.sha_short }}</span>
+ </div>
+ <div class="shortcuts">press [?] for shortcuts</div>
</nav>
{% endblock %}
{% block content %}
-<div style="padding:16px 0;border-bottom:1px solid var(--rule)">
-<div style="font-family:var(--font-mono);font-size:15px;line-height:1.6">
-<span style="color:{{ run.state_color }}">{{ run.state }}</span> · <span style="color:var(--accent)">{{ run.sha_short }}</span> · {{ run.ref_short }}
-</div>
-<div style="font-family:var(--font-mono);font-size:12px;color:var(--mutedFaint);margin-top:4px">
-queued {{ run.queued }} · started {{ run.started }} · finished {{ run.finished }} · {{ run.duration }}
-</div>
+<div class="ci-meta">
+ <div class="ci-meta-primary">
+ <span class="{{ run.state_color }}">{{ run.state }}</span>
+ · <span class="c-accent">{{ run.sha_short }}</span>
+ · {{ run.ref_short }}
+ </div>
+ <div class="ci-meta-secondary">
+ queued {{ run.queued }} · started {{ run.started }} · finished {{ run.finished }} · {{ run.duration }}
+ </div>
</div>
+
{% for job in jobs %}
-<div style="margin:24px 0 0">
-<div style="font-family:var(--font-mono);font-size:13px;font-weight:500;padding:8px 0;border-bottom:1px solid var(--rule2)">
-<span style="color:{{ job.state_color }}">{{ job.state }}</span> · {{ job.job_id }} · {{ job.duration }}{{ job.exit_str }}
-</div>
-{% for sh in job.sh_events %}
-<div style="margin:8px 0">
-<div style="font-family:var(--font-mono);font-size:11px;color:var(--mutedFaint);margin-bottom:2px">
-sh-{{ sh.index }} · {{ sh.duration }} · exit {{ sh.exit_code }}
-</div>
-<div style="font-family:var(--font-mono);font-size:12px;color:var(--muted);margin-bottom:4px">
-{{ sh.cmd_display }}
-</div>
-{% if !sh.log_content.is_empty() %}
-<pre style="font-family:var(--font-mono);font-size:12px;line-height:1.65;background:var(--code);color:var(--ink);padding:10px 14px;border-left:2px solid var(--accent);overflow:auto;margin:0 0 8px">{{ sh.log_content }}</pre>
-{% endif %}
-</div>
-{% endfor %}
+<div class="ci-job">
+ <div class="ci-job-header">
+ <span class="{{ job.state_color }}">{{ job.state }}</span>
+ · {{ job.job_id }} · {{ job.duration }}{{ job.exit_str }}
+ </div>
+ {% for sh in job.sh_events %}
+ <div class="ci-sh">
+ <div class="ci-sh-meta">
+ sh-{{ sh.index }} · {{ sh.duration }} · exit {{ sh.exit_code }}
+ </div>
+ <div class="ci-sh-cmd">{{ sh.cmd_display }}</div>
+ {% if !sh.log_content.is_empty() %}
+ <pre class="ci-sh-log">{{ sh.log_content }}</pre>
+ {% endif %}
+ </div>
+ {% endfor %}
</div>
{% else %}
-<div style="padding:16px 0;color:var(--muted)">no jobs recorded</div>
+<div class="ci-empty">no jobs recorded</div>
{% endfor %}
{% endblock %}
diff --git a/templates/ci/run_list.html b/templates/ci/run_list.html
index 3871c32..4ddd66c 100644
--- a/templates/ci/run_list.html
+++ b/templates/ci/run_list.html
@@ -3,36 +3,42 @@
{% block title %}ci · {{ repo }}{% endblock %}
{% block nav %}
-<nav style="padding:14px 56px;border-bottom:1px solid var(--rule);font-family:var(--font-mono);font-size:14px;font-weight:500;letter-spacing:-0.2px;display:flex;justify-content:space-between;align-items:center">
-<div><span style="color:var(--mutedFaint)">quire</span> <span style="color:var(--rule2)">/</span> <span>{{ repo }}</span> <span style="color:var(--rule2)">/</span> <span style="color:var(--muted)">ci</span></div>
-<div style="font-size:11px;color:var(--mutedFaint)">press [?] for shortcuts</div>
+<nav class="page-nav">
+ <div class="breadcrumbs">
+ <span class="crumb-faint">quire</span>
+ <span class="sep">/</span>
+ <span>{{ repo }}</span>
+ <span class="sep">/</span>
+ <span class="crumb-muted">ci</span>
+ </div>
+ <div class="shortcuts">press [?] for shortcuts</div>
</nav>
{% endblock %}
{% block content %}
-<h2 style="font-family:var(--font-mono);font-size:19px;font-weight:600;margin:0 0 16px">ci runs</h2>
-<table style="width:100%;border-collapse:collapse;font-family:var(--font-mono);font-size:12.5px;line-height:1.6">
-<thead>
-<tr style="border-bottom:1px solid var(--rule2)">
- <th style="text-align:left;padding:6px 8px;font-weight:400;color:var(--mutedFaint)"></th>
- <th style="text-align:left;padding:6px 8px;font-weight:400;color:var(--mutedFaint)">sha</th>
- <th style="text-align:left;padding:6px 8px;font-weight:400;color:var(--mutedFaint)">ref</th>
- <th style="text-align:left;padding:6px 8px;font-weight:400;color:var(--mutedFaint)">queued</th>
- <th style="text-align:left;padding:6px 8px;font-weight:400;color:var(--mutedFaint)">duration</th>
-</tr>
-</thead>
-<tbody style="border-top:1px solid var(--rule)">
-{% for run in runs %}
-<tr>
- <td style="padding:6px 8px"><span style="display:inline-block;width:6px;height:6px;border-radius:3px;background:{{ run.state_color }}"></span></td>
- <td style="padding:6px 8px"><a href="/repo/{{ repo }}/ci/{{ run.id }}" style="color:var(--accent);text-decoration:none;border-bottom:1px dotted var(--rule2)">{{ run.sha_short }}</a></td>
- <td style="padding:6px 8px">{{ run.ref_short }}</td>
- <td style="padding:6px 8px">{{ run.queued }}</td>
- <td style="padding:6px 8px">{{ run.duration }}</td>
-</tr>
-{% else %}
-<tr><td colspan="5" style="padding:16px;color:var(--muted)">no runs yet</td></tr>
-{% endfor %}
-</tbody>
+<h2 class="ci-heading">ci runs</h2>
+<table class="ci-table">
+ <thead>
+ <tr>
+ <th></th>
+ <th>sha</th>
+ <th>ref</th>
+ <th>queued</th>
+ <th>duration</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for run in runs %}
+ <tr>
+ <td><span class="ci-status-dot {{ run.state_color }}"></span></td>
+ <td><a href="/repo/{{ repo }}/ci/{{ run.id }}" class="ci-sha-link">{{ run.sha_short }}</a></td>
+ <td>{{ run.ref_short }}</td>
+ <td>{{ run.queued }}</td>
+ <td>{{ run.duration }}</td>
+ </tr>
+ {% else %}
+ <tr><td colspan="5" class="empty">no runs yet</td></tr>
+ {% endfor %}
+ </tbody>
</table>
{% endblock %}
diff --git a/templates/error.html b/templates/error.html
index b576e0e..b963f8b 100644
--- a/templates/error.html
+++ b/templates/error.html
@@ -3,13 +3,19 @@
{% block title %}{{ title }}{% endblock %}
{% block nav %}
-<nav style="padding:14px 56px;border-bottom:1px solid var(--rule);font-family:var(--font-mono);font-size:14px;font-weight:500;letter-spacing:-0.2px;display:flex;justify-content:space-between;align-items:center">
-<div><span style="color:var(--mutedFaint)">quire</span> <span style="color:var(--rule2)">/</span> <span>{{ repo }}</span> <span style="color:var(--rule2)">/</span> <span style="color:var(--muted)">error</span></div>
-<div style="font-size:11px;color:var(--mutedFaint)">press [?] for shortcuts</div>
+<nav class="page-nav">
+ <div class="breadcrumbs">
+ <span class="crumb-faint">quire</span>
+ <span class="sep">/</span>
+ <span>{{ repo }}</span>
+ <span class="sep">/</span>
+ <span class="crumb-muted">error</span>
+ </div>
+ <div class="shortcuts">press [?] for shortcuts</div>
</nav>
{% endblock %}
{% block content %}
-<p style="color:var(--bad);font-family:var(--font-mono)">{{ title }}</p>
-<pre style="font-family:var(--font-mono);font-size:12px;background:var(--code);padding:14px 18px;overflow:auto">{{ detail }}</pre>
+<p class="error-title">{{ title }}</p>
+<pre class="error-detail">{{ detail }}</pre>
{% endblock %}