fix(frontend): finish the token migration — raw grey classes -> theme tokens

UI_REDESIGN chunk 4 was never finished: 39 app/component files still used
raw Tailwind greys, kept readable in light mode only by the !important
override sheet. In dark mode text-gray-600 is ~2.3:1 on surface, and the
light override maps it to #94a3b8 (~2.6:1 on white) — unreadable in both.

Mechanical mapping (886 classes): gray-100/200 -> ink, 300/400 -> ink-2,
500/600/700 -> ink-muted, bg 950/900/800 -> page/surface/raised,
border 800 -> line, 700/600 -> line-strong, plus hover/divide/placeholder.
Alpha suffixes kept (tokens are color-mix, so /60 works). bg-gray-700,
text-white and marketing pages left alone. text-[9px] -> text-[10px].

Frontend unverified: no Node on the authoring box (#207).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Logan Cusano
2026-10-25 21:29:13 -04:00
co-authored by Claude Opus 5.5
parent 3968b49b80
commit bf206334e3
39 changed files with 655 additions and 655 deletions
+13 -13
View File
@@ -125,7 +125,7 @@ export default function NodeDetailPage() {
}
if (!node) {
return <p className="text-gray-500 font-mono text-sm">Loading node…</p>;
return <p className="text-ink-muted font-mono text-sm">Loading node…</p>;
}
const system = systemMap[node.assigned_system_id ?? ""];
@@ -135,7 +135,7 @@ export default function NodeDetailPage() {
<div className="flex items-start justify-between">
<div>
<h1 className="text-xl font-bold text-white font-mono">{node.name}</h1>
<p className="text-gray-500 text-sm font-mono">{node.node_id}</p>
<p className="text-ink-muted text-sm font-mono">{node.node_id}</p>
</div>
<StatusBadge status={node.status} />
</div>
@@ -147,7 +147,7 @@ export default function NodeDetailPage() {
<span className="text-base">⚠</span>
<span>Local System Override Active</span>
</div>
<p className="text-gray-400 text-xs leading-relaxed">
<p className="text-ink-2 text-xs leading-relaxed">
This node is operating on a local system override.
{node.override_timeout_at ? (
<> Resets automatically on: <span className="text-white font-bold">{new Date(node.override_timeout_at).toLocaleString()}</span>.</>
@@ -189,7 +189,7 @@ export default function NodeDetailPage() {
)}
{/* Info */}
<div className="bg-gray-900 border border-gray-800 rounded-lg divide-y divide-gray-800 font-mono text-sm">
<div className="bg-surface border border-line rounded-lg divide-y divide-line font-mono text-sm">
{[
["System", system?.name ?? "Unassigned"],
["Location", `${node.lat}, ${node.lon}`],
@@ -208,12 +208,12 @@ export default function NodeDetailPage() {
...(node.node_type !== "portable" ? [["Enforce Timeout", node.enforce_override_timeout ? "Yes" : "No"]] : []),
].map(([label, value]) => (
<div key={label} className="flex justify-between px-4 py-2.5">
<span className="text-gray-500">{label}</span>
<span className="text-gray-200">{value}</span>
<span className="text-ink-muted">{label}</span>
<span className="text-ink">{value}</span>
</div>
))}
<div className="flex justify-between px-4 py-2.5">
<span className="text-gray-500">Approval</span>
<span className="text-ink-muted">Approval</span>
<ApprovalBadge status={node.approval_status ?? null} />
</div>
</div>
@@ -232,7 +232,7 @@ export default function NodeDetailPage() {
<button
onClick={handleReject}
disabled={approving}
className="px-4 py-2 bg-red-900 hover:bg-red-800 disabled:opacity-50 text-gray-300 rounded-lg text-sm font-mono transition-colors"
className="px-4 py-2 bg-red-900 hover:bg-red-800 disabled:opacity-50 text-ink-2 rounded-lg text-sm font-mono transition-colors"
>
Reject
</button>
@@ -250,7 +250,7 @@ export default function NodeDetailPage() {
<button
disabled={sending}
onClick={() => sendCommand("op25_restart")}
className="px-4 py-2 bg-gray-800 hover:bg-gray-700 text-gray-300 rounded-lg text-sm font-mono transition-colors disabled:opacity-50"
className="px-4 py-2 bg-raised hover:bg-gray-700 text-ink-2 rounded-lg text-sm font-mono transition-colors disabled:opacity-50"
>
Restart OP25
</button>
@@ -258,7 +258,7 @@ export default function NodeDetailPage() {
<button
disabled={sending}
onClick={handleRequestUpdate}
className="px-4 py-2 bg-gray-800 hover:bg-gray-700 text-gray-300 rounded-lg text-sm font-mono transition-colors disabled:opacity-50"
className="px-4 py-2 bg-raised hover:bg-gray-700 text-ink-2 rounded-lg text-sm font-mono transition-colors disabled:opacity-50"
>
Update Node Software
</button>
@@ -283,16 +283,16 @@ export default function NodeDetailPage() {
{/* Recent calls */}
<section>
<h2 className="text-sm font-semibold text-gray-400 uppercase tracking-wider mb-3">Recent Calls</h2>
<h2 className="text-sm font-semibold text-ink-2 uppercase tracking-wider mb-3">Recent Calls</h2>
{/* Gate A / A2 (server-26#46) — each row expands to a transcript. */}
{nodeCalls.length > 0 && <MachineOutputNotice variant="inline" className="mb-3" />}
{nodeCalls.length === 0 ? (
<p className="text-gray-600 text-sm font-mono">No calls recorded from this node.</p>
<p className="text-ink-muted text-sm font-mono">No calls recorded from this node.</p>
) : (
<div className="overflow-x-auto">
<table className="w-full text-sm">
<thead>
<tr className="text-xs text-gray-500 uppercase tracking-wider border-b border-gray-800">
<tr className="text-xs text-ink-muted uppercase tracking-wider border-b border-line">
<th className="px-4 py-2 text-left">Time</th>
<th className="px-4 py-2 text-left">Talkgroup</th>
<th className="px-4 py-2 text-left">System</th>
+4 -4
View File
@@ -65,7 +65,7 @@ export default function NodesPage() {
<button
onClick={handleUpdateAll}
disabled={updatingAll}
className="px-4 py-2 bg-gray-800 hover:bg-gray-700 text-gray-300 rounded-lg text-sm font-mono transition-colors disabled:opacity-50"
className="px-4 py-2 bg-raised hover:bg-gray-700 text-ink-2 rounded-lg text-sm font-mono transition-colors disabled:opacity-50"
>
{updatingAll ? "Requesting…" : "Update All Nodes"}
</button>
@@ -88,13 +88,13 @@ export default function NodesPage() {
)}
<div className="space-y-2">
<h2 className="text-sm font-semibold text-gray-400 uppercase tracking-wider">
<h2 className="text-sm font-semibold text-ink-2 uppercase tracking-wider">
All Nodes ({nodes.length})
</h2>
{loading ? (
<p className="text-gray-600 text-sm font-mono">Loading…</p>
<p className="text-ink-muted text-sm font-mono">Loading…</p>
) : nodes.length === 0 ? (
<p className="text-gray-600 text-sm font-mono">No nodes registered yet. Boot a Pi to get started.</p>
<p className="text-ink-muted text-sm font-mono">No nodes registered yet. Boot a Pi to get started.</p>
) : (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{nodes.map((n) => (