style(dashboard): extract inline styles to tokenized CSS files

Move inline <style> blocks from simple.html and integrations.html into
external CSS files (wizard.css, integrations.css), replacing all
hard-coded pixel values with design tokens from tokens.css. Remove
inline style attributes in favor of CSS classes. All 5 dashboard pages
now share one design system via Radix dark tokens (§8e).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
jedarden 2026-04-24 15:45:24 -04:00
parent e21bd1db9f
commit 18b7d7d1d0
4 changed files with 534 additions and 297 deletions

View file

@ -0,0 +1,271 @@
/*
Integrations Page MQTT & Webhook configuration (§8e)
*/
.integrations-header {
margin-bottom: var(--space-6);
}
.integrations-header h1 {
font-size: var(--text-2xl);
font-weight: var(--fw-heading);
margin: 0 0 var(--space-2) 0;
}
.integrations-header p {
color: var(--text-muted);
font-size: var(--text-sm);
margin: 0;
}
.integrations-container {
max-width: 800px;
margin: 0 auto;
padding: var(--space-5);
}
.integration-card {
background: var(--bg-card);
border-radius: var(--radius-card);
padding: var(--space-6);
margin-bottom: var(--space-5);
border: 1px solid var(--border-default);
}
.integration-card-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: var(--space-4);
}
.integration-card-title {
font-size: var(--text-lg);
font-weight: var(--fw-heading);
display: flex;
align-items: center;
gap: var(--space-3);
}
.integration-status {
display: flex;
align-items: center;
gap: var(--space-2);
font-size: var(--text-sm);
}
.status-dot {
width: var(--space-2);
height: var(--space-2);
border-radius: 50%;
background: var(--slate-7);
}
.status-dot.connected {
background: var(--ok);
box-shadow: 0 0 var(--space-2) var(--ok);
}
.status-dot.disconnected {
background: var(--alert);
}
.integration-description {
color: var(--text-muted);
font-size: var(--text-sm);
margin-bottom: var(--space-5);
line-height: var(--lh-body);
}
.form-group {
margin-bottom: var(--space-4);
}
.form-group label {
display: block;
font-size: var(--text-sm);
font-weight: 500;
margin-bottom: var(--space-1);
color: var(--text-secondary);
}
.form-group input[type="text"],
.form-group input[type="password"],
.form-group input[type="url"] {
width: 100%;
padding: var(--space-2) var(--space-3);
background: var(--bg-input);
border: 1px solid var(--border-default);
border-radius: var(--radius-control);
color: var(--text-primary);
font-size: var(--text-sm);
box-sizing: border-box;
}
.form-group input:focus {
outline: none;
border-color: var(--blue-9);
}
.form-group input::placeholder {
color: var(--slate-7);
}
.form-hint {
font-size: var(--text-xs);
color: var(--slate-7);
margin-top: var(--space-1);
}
.checkbox-group {
display: flex;
align-items: center;
gap: var(--space-2);
}
.checkbox-group input[type="checkbox"] {
width: var(--space-4);
height: var(--space-4);
cursor: pointer;
}
.checkbox-group label {
margin: 0;
cursor: pointer;
}
.btn {
padding: var(--space-2) var(--space-5);
border-radius: var(--radius-control);
border: none;
font-size: var(--text-sm);
font-weight: 500;
cursor: pointer;
transition: background var(--transition-base);
}
.btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.btn-primary {
background: var(--blue-9);
color: var(--text-on-accent);
}
.btn-primary:hover:not(:disabled) {
background: var(--blue-10);
}
.btn-secondary {
background: var(--bg-hover);
color: var(--text-primary);
}
.btn-secondary:hover:not(:disabled) {
background: var(--bg-active);
}
.btn-full {
width: 100%;
}
.btn-margin-top {
margin-top: var(--space-3);
}
.button-group {
display: flex;
gap: var(--space-2);
margin-top: var(--space-3);
}
.button-group .btn {
flex: 1;
}
.loading-overlay {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: var(--overlay-strong);
align-items: center;
justify-content: center;
z-index: 1000;
}
.loading-overlay.active {
display: flex;
}
.spinner {
width: var(--space-10);
height: var(--space-10);
border: 3px solid var(--slate-5);
border-top-color: var(--blue-9);
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.toast {
position: fixed;
bottom: var(--space-5);
right: var(--space-5);
background: var(--slate-4);
color: var(--text-primary);
padding: var(--space-3) var(--space-5);
border-radius: var(--radius-control);
box-shadow: var(--shadow-lg);
display: none;
z-index: 1001;
}
.toast.show {
display: block;
animation: slideIn var(--transition-base) ease;
}
.toast.success {
background: var(--ok);
}
.toast.error {
background: var(--alert);
}
@keyframes slideIn {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
.back-link {
display: inline-flex;
align-items: center;
gap: var(--space-2);
color: var(--blue-9);
text-decoration: none;
font-size: var(--text-sm);
margin-bottom: var(--space-4);
}
.back-link:hover {
text-decoration: underline;
}
.hidden {
display: none;
}

257
dashboard/css/wizard.css Normal file
View file

@ -0,0 +1,257 @@
/*
Onboarding Wizard inline-to-external extraction from simple.html
*/
#wizard-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: var(--overlay-strong);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
#wizard-card {
background: var(--bg-card);
border-radius: var(--radius-modal);
padding: var(--space-6) var(--space-8) var(--space-5);
max-width: 560px;
width: 92%;
max-height: 90vh;
overflow-y: auto;
box-shadow: var(--shadow-xl);
}
#wizard-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: var(--space-5);
}
#wizard-header h1 {
font-size: var(--text-lg);
font-weight: var(--fw-heading);
color: var(--text-primary);
}
.wizard-close {
background: none;
border: none;
color: var(--text-muted);
font-size: var(--text-2xl);
cursor: pointer;
padding: 0 var(--space-1);
line-height: 1;
}
.wizard-close:hover {
color: var(--text-primary);
}
#wizard-steps {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: var(--space-6);
gap: 0;
}
.wizard-step-dot {
width: var(--space-6);
height: var(--space-6);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: var(--text-xs);
font-weight: var(--fw-heading);
background: var(--bg-active);
color: var(--slate-7);
transition: all var(--transition-base);
flex-shrink: 0;
}
.wizard-step-dot.active {
background: var(--color-primary);
color: var(--text-on-accent);
box-shadow: 0 0 var(--space-3) var(--blue-border);
}
.wizard-step-dot.completed {
background: var(--ok);
color: var(--text-on-accent);
}
.wizard-step-line {
width: var(--space-5);
height: 2px;
background: var(--bg-active);
flex-shrink: 0;
}
.wizard-step-line.completed {
background: var(--ok);
}
#wizard-content {
min-height: 180px;
}
.wizard-step-content {
text-align: center;
}
.wizard-step-content h2 {
font-size: var(--text-lg);
margin-bottom: var(--space-2);
color: var(--text-primary);
}
.wizard-step-content p {
color: var(--text-secondary);
font-size: var(--text-sm);
line-height: var(--lh-body);
margin-bottom: var(--space-3);
}
.wizard-muted {
color: var(--text-muted) !important;
font-size: var(--text-xs) !important;
}
.wizard-center-msg {
padding: var(--space-6) 0;
text-align: center;
}
.wizard-center-msg p {
color: var(--text-muted);
margin-top: var(--space-3);
}
.wizard-error {
color: var(--alert);
font-size: var(--text-sm);
padding: var(--space-3);
background: var(--alert-bg);
border-radius: var(--radius-control);
text-align: left;
margin-top: var(--space-2);
}
.wizard-success {
color: var(--ok);
font-size: var(--text-sm);
font-weight: 500;
}
.wizard-icon-large {
font-size: var(--text-2xl);
margin-bottom: var(--space-3);
}
.wizard-list {
text-align: left;
color: var(--text-secondary);
font-size: var(--text-sm);
line-height: 1.8;
margin: var(--space-3) 0;
padding-left: var(--space-5);
}
.wizard-spinner {
display: inline-block;
width: var(--space-8);
height: var(--space-8);
border: 3px solid var(--blue-border);
border-top-color: var(--color-primary);
border-radius: 50%;
animation: wizard-spin .8s linear infinite;
}
.spinner {
display: inline-block;
width: var(--space-8);
height: var(--space-8);
border: 3px solid var(--blue-border);
border-top-color: var(--color-primary);
border-radius: 50%;
animation: wizard-spin .8s linear infinite;
}
@keyframes wizard-spin {
to { transform: rotate(360deg); }
}
.wizard-progress {
margin: var(--space-4) 0;
}
.progress-bar {
width: 100%;
height: var(--space-2);
background: var(--bg-active);
border-radius: var(--space-1);
overflow: hidden;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, var(--blue-9), var(--blue-10));
border-radius: var(--space-1);
transition: width var(--transition-base);
width: 0%;
}
.wizard-progress p {
font-size: var(--text-xs);
color: var(--text-muted);
margin-top: var(--space-2);
text-align: center;
}
.wizard-btn {
padding: var(--space-3) var(--space-6);
border-radius: var(--radius-control);
font-size: var(--text-sm);
font-weight: 500;
cursor: pointer;
border: none;
transition: background var(--transition-base), opacity var(--transition-base);
}
.wizard-btn:disabled {
opacity: .5;
cursor: not-allowed;
}
.wizard-btn-primary {
background: var(--color-primary);
color: var(--slate-1);
}
.wizard-btn-primary:hover:not(:disabled) {
background: var(--color-primary-hover);
}
.wizard-btn-secondary {
background: var(--bg-hover);
color: var(--text-secondary);
border: 1px solid var(--border-strong);
}
.wizard-btn-secondary:hover:not(:disabled) {
background: var(--bg-active);
}
#wizard-nav {
display: flex;
justify-content: space-between;
margin-top: var(--space-5);
gap: var(--space-3);
}

View file

@ -7,261 +7,7 @@
<link rel="stylesheet" href="css/tokens.css">
<link rel="stylesheet" href="css/layout.css">
<meta name="theme-color" content="#18191b">
<style>
.integrations-header {
margin-bottom: var(--space-6);
}
.integrations-header h1 {
font-size: var(--text-2xl);
font-weight: var(--fw-heading);
margin: 0 0 var(--space-2) 0;
}
.integrations-header p {
color: var(--text-muted);
font-size: var(--text-sm);
margin: 0;
}
.integration-card {
background: var(--bg-card);
border-radius: var(--radius-card);
padding: var(--space-6);
margin-bottom: var(--space-5);
border: 1px solid var(--border-default);
}
.integration-card-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: var(--space-4);
}
.integration-card-title {
font-size: var(--text-lg);
font-weight: var(--fw-heading);
display: flex;
align-items: center;
gap: var(--space-3);
}
.integration-status {
display: flex;
align-items: center;
gap: var(--space-2);
font-size: var(--text-sm);
}
.status-dot {
width: var(--space-2);
height: var(--space-2);
border-radius: 50%;
background: var(--slate-7);
}
.status-dot.connected {
background: var(--ok);
box-shadow: 0 0 var(--space-2) var(--ok);
}
.status-dot.disconnected {
background: var(--alert);
}
.integration-description {
color: var(--text-muted);
font-size: var(--text-sm);
margin-bottom: var(--space-5);
line-height: var(--lh-body);
}
.form-group {
margin-bottom: var(--space-4);
}
.form-group label {
display: block;
font-size: var(--text-sm);
font-weight: 500;
margin-bottom: var(--space-1);
color: var(--text-secondary);
}
.form-group input[type="text"],
.form-group input[type="password"],
.form-group input[type="url"] {
width: 100%;
padding: var(--space-2) var(--space-3);
background: var(--bg-input);
border: 1px solid var(--border-default);
border-radius: var(--radius-control);
color: var(--text-primary);
font-size: var(--text-sm);
box-sizing: border-box;
}
.form-group input:focus {
outline: none;
border-color: var(--blue-9);
}
.form-group input::placeholder {
color: var(--slate-7);
}
.form-hint {
font-size: var(--text-xs);
color: var(--slate-7);
margin-top: var(--space-1);
}
.checkbox-group {
display: flex;
align-items: center;
gap: var(--space-2);
}
.checkbox-group input[type="checkbox"] {
width: 18px;
height: 18px;
cursor: pointer;
}
.checkbox-group label {
margin: 0;
cursor: pointer;
}
.btn {
padding: var(--space-2) var(--space-5);
border-radius: var(--radius-control);
border: none;
font-size: var(--text-sm);
font-weight: 500;
cursor: pointer;
transition: background var(--transition-base);
}
.btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.btn-primary {
background: var(--blue-9);
color: var(--text-on-accent);
}
.btn-primary:hover:not(:disabled) {
background: var(--blue-10);
}
.btn-secondary {
background: var(--bg-hover);
color: var(--text-primary);
}
.btn-secondary:hover:not(:disabled) {
background: var(--bg-active);
}
.btn-full {
width: 100%;
}
.button-group {
display: flex;
gap: var(--space-2);
margin-top: var(--space-3);
}
.button-group .btn {
flex: 1;
}
.loading-overlay {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: var(--overlay-strong);
align-items: center;
justify-content: center;
z-index: 1000;
}
.loading-overlay.active {
display: flex;
}
.spinner {
width: 40px;
height: 40px;
border: 3px solid var(--slate-5);
border-top-color: var(--blue-9);
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.toast {
position: fixed;
bottom: var(--space-5);
right: var(--space-5);
background: var(--slate-4);
color: var(--text-primary);
padding: var(--space-3) var(--space-5);
border-radius: var(--radius-control);
box-shadow: 0 4px 12px var(--shadow);
display: none;
z-index: 1001;
}
.toast.show {
display: block;
animation: slideIn 0.3s ease;
}
.toast.success {
background: var(--ok);
}
.toast.error {
background: var(--alert);
}
@keyframes slideIn {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
.back-link {
display: inline-flex;
align-items: center;
gap: var(--space-2);
color: var(--blue-9);
text-decoration: none;
font-size: var(--text-sm);
margin-bottom: var(--space-4);
}
.back-link:hover {
text-decoration: underline;
}
</style>
<link rel="stylesheet" href="css/integrations.css">
</head>
<body class="has-mobile-nav">
<div class="app-shell app-shell--full">
@ -278,7 +24,7 @@
<!-- ── Main content ── -->
<main class="app-main">
<div style="max-width:800px;margin:0 auto;padding:var(--space-5);">
<div class="integrations-container">
<a href="index.html" class="back-link">← Back to Dashboard</a>
@ -370,7 +116,7 @@
<label for="webhook-enabled">Enable System Webhook</label>
</div>
<div class="form-group" id="webhook-url-group" style="display: none;">
<div class="form-group hidden" id="webhook-url-group">
<label for="webhook-url">Webhook URL</label>
<input type="url" id="webhook-url" placeholder="https://your-server.com/spaxel-webhook">
<div class="form-hint">Events will be POSTed as JSON with X-Spaxel-Event header</div>
@ -378,7 +124,7 @@
<button type="submit" class="btn btn-primary btn-full" id="save-webhook-btn">Save Webhook Settings</button>
<button type="button" class="btn btn-secondary btn-full" id="test-webhook-btn" style="margin-top: 12px;">Test Webhook</button>
<button type="button" class="btn btn-secondary btn-full btn-margin-top" id="test-webhook-btn">Test Webhook</button>
</form>
</div>
</div>
@ -427,7 +173,7 @@
webhookEnabled.addEventListener('change', function() {
const urlGroup = document.getElementById('webhook-url-group');
if (urlGroup) {
urlGroup.style.display = this.checked ? '' : 'none';
urlGroup.classList.toggle('hidden', !this.checked);
}
});
}

View file

@ -8,44 +8,7 @@
<link rel="stylesheet" href="css/layout.css">
<link rel="stylesheet" href="css/simple.css">
<link rel="stylesheet" href="css/notifications.css">
<style>
#wizard-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:var(--overlay-strong);display:flex;align-items:center;justify-content:center;z-index:1000}
#wizard-card{background:var(--bg-card);border-radius:var(--radius-modal);padding:28px 32px 20px;max-width:560px;width:92%;max-height:90vh;overflow-y:auto;box-shadow:var(--shadow-xl)}
#wizard-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:var(--space-5)}
#wizard-header h1{font-size:var(--text-lg);font-weight:var(--fw-heading);color:var(--text-primary)}
.wizard-close{background:none;border:none;color:var(--text-muted);font-size:24px;cursor:pointer;padding:0 4px;line-height:1}
.wizard-close:hover{color:var(--text-primary)}
#wizard-steps{display:flex;align-items:center;justify-content:center;margin-bottom:var(--space-6);gap:0}
.wizard-step-dot{width:28px;height:28px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:var(--fw-heading);background:var(--bg-active);color:var(--slate-7);transition:all .3s;flex-shrink:0}
.wizard-step-dot.active{background:var(--color-primary);color:var(--text-on-accent);box-shadow:0 0 12px var(--blue-border)}
.wizard-step-dot.completed{background:var(--ok);color:var(--text-on-accent)}
.wizard-step-line{width:20px;height:2px;background:var(--bg-active);flex-shrink:0}
.wizard-step-line.completed{background:var(--ok)}
#wizard-content{min-height:180px}
.wizard-step-content{text-align:center}
.wizard-step-content h2{font-size:18px;margin-bottom:var(--space-2);color:var(--text-primary)}
.wizard-step-content p{color:var(--text-secondary);font-size:var(--text-sm);line-height:var(--lh-body);margin-bottom:var(--space-3)}
.wizard-muted{color:var(--text-muted)!important;font-size:13px!important}
.wizard-center-msg{padding:var(--space-6) 0;text-align:center}
.wizard-center-msg p{color:var(--text-muted);margin-top:var(--space-3)}
.wizard-error{color:var(--alert);font-size:13px;padding:10px;background:var(--alert-bg);border-radius:var(--radius-control);text-align:left;margin-top:var(--space-2)}
.wizard-success{color:var(--ok);font-size:var(--text-sm);font-weight:500}
.wizard-icon-large{font-size:48px;margin-bottom:var(--space-3)}
.wizard-list{text-align:left;color:var(--text-secondary);font-size:13px;line-height:1.8;margin:var(--space-3) 0;padding-left:20px}
.spinner{display:inline-block;width:32px;height:32px;border:3px solid var(--blue-border);border-top-color:var(--color-primary);border-radius:50%;animation:wizard-spin .8s linear infinite}
@keyframes wizard-spin{to{transform:rotate(360deg)}}
.wizard-progress{margin:var(--space-4) 0}
.progress-bar{width:100%;height:8px;background:var(--bg-active);border-radius:4px;overflow:hidden}
.progress-fill{height:100%;background:linear-gradient(90deg,var(--blue-9),var(--blue-10));border-radius:4px;transition:width .3s;width:0%}
.wizard-progress p{font-size:var(--text-xs);color:var(--text-muted);margin-top:6px;text-align:center}
.wizard-btn{padding:10px 24px;border-radius:var(--radius-control);font-size:var(--text-sm);font-weight:500;cursor:pointer;border:none;transition:background .2s,opacity .2s}
.wizard-btn:disabled{opacity:.5;cursor:not-allowed}
.wizard-btn-primary{background:var(--color-primary);color:var(--slate-1)}
.wizard-btn-primary:hover:not(:disabled){background:var(--color-primary-hover)}
.wizard-btn-secondary{background:var(--bg-hover);color:var(--text-secondary);border:1px solid var(--border-strong)}
.wizard-btn-secondary:hover:not(:disabled){background:var(--bg-active)}
#wizard-nav{display:flex;justify-content:space-between;margin-top:var(--space-5);gap:var(--space-3)}
</style>
<link rel="stylesheet" href="css/wizard.css">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="theme-color" content="#18191b">