ui: brand-consistent button and card styling

Replace traffic light colors (green/yellow/red) with brand palette:
- Primary actions: blue gradient
- Secondary actions: ghost/outline
- Destructive actions: ghost with red hover (modals keep filled red)
- Running cards: blue glow instead of green border
- Bottom-aligned buttons via flexbox column layout

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-25 21:32:44 +01:00
parent d7e5332a11
commit f6b09ca99e
2 changed files with 64 additions and 27 deletions
+9
View File
@@ -1,5 +1,14 @@
## Changelog ## Changelog
### v0.31.6 — UI: Brand-consistent button & card styling (2026-02-25)
#### Changed
- **Buttons**: Replaced traffic light colors (green/yellow/red) with brand-consistent palette — primary actions use blue gradient, secondary actions use ghost/outline, destructive actions show red tint on hover only (modal confirmations keep filled red)
- **Card borders**: Running apps now show a subtle blue glow instead of green top border; all other states have neutral borders
- **Status badges**: Running state badge uses brand blue instead of green
- **Button alignment**: Cards use flexbox column layout with `margin-top: auto` on actions — buttons always align to the bottom regardless of card content height
- **Dashboard cards**: Left border indicator changed from green to blue for running apps
### v0.31.5 — Fix Nextcloud-OnlyOffice callback URL + trusted_domains (2026-02-25) ### v0.31.5 — Fix Nextcloud-OnlyOffice callback URL + trusted_domains (2026-02-25)
#### Fixed #### Fixed
+55 -27
View File
@@ -280,20 +280,19 @@ h3 {
border-radius: var(--radius); border-radius: var(--radius);
padding: 1rem 1.25rem; padding: 1rem 1.25rem;
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
border-left: 4px solid var(--gray); border-left: 4px solid var(--border-color);
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
transition: border-color 0.3s ease, transform 0.2s ease; transition: border-color 0.3s ease, transform 0.2s ease, box-shadow 0.3s ease;
} }
.stack-card:hover { .stack-card:hover {
border-color: var(--accent-blue); border-color: var(--accent-blue);
} }
.stack-state-green { border-left-color: var(--green); } .stack-card.stack-state-green {
.stack-state-red { border-left-color: var(--red); } border-left-color: var(--accent-blue);
.stack-state-yellow { border-left-color: var(--yellow); } box-shadow: 0 0 12px rgba(0, 136, 204, 0.1);
.stack-state-orange { border-left-color: var(--orange); } }
.stack-state-gray { border-left-color: var(--gray); }
.stack-info { .stack-info {
display: flex; display: flex;
align-items: center; align-items: center;
@@ -339,17 +338,22 @@ h3 {
border-radius: var(--radius); border-radius: var(--radius);
padding: 1.25rem; padding: 1.25rem;
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
border-top: 4px solid var(--gray); transition: border-color 0.3s ease, transform 0.3s ease, box-shadow 0.3s ease;
transition: border-color 0.3s ease, transform 0.3s ease; display: flex;
flex-direction: column;
} }
.stack-detail-card:hover { .stack-detail-card:hover {
border-color: var(--accent-blue); border-color: var(--accent-blue);
transform: translateY(-2px); transform: translateY(-2px);
} }
.stack-detail-card.stack-state-green { border-top-color: var(--green); } .stack-detail-card.stack-state-green {
.stack-detail-card.stack-state-red { border-top-color: var(--red); } border-color: rgba(0, 136, 204, 0.25);
.stack-detail-card.stack-state-orange { border-top-color: var(--orange); } box-shadow: 0 0 16px rgba(0, 136, 204, 0.12), 0 0 4px rgba(0, 136, 204, 0.08);
.stack-detail-card.stack-state-yellow { border-top-color: var(--yellow); } }
.stack-detail-card.stack-state-green:hover {
border-color: var(--accent-blue);
box-shadow: 0 0 20px rgba(0, 136, 204, 0.2), 0 0 6px rgba(0, 136, 204, 0.12);
}
.stack-detail-header { .stack-detail-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@@ -375,12 +379,12 @@ h3 {
font-weight: 600; font-weight: 600;
white-space: nowrap; white-space: nowrap;
} }
.state-green { background: var(--green-bg); color: var(--green); } .state-green { background: rgba(0, 136, 204, 0.12); color: var(--accent-light); }
.state-red { background: var(--red-bg); color: var(--red); } .state-red { background: var(--red-bg); color: var(--red); }
.state-yellow { background: var(--yellow-bg); color: var(--yellow); } .state-yellow { background: var(--yellow-bg); color: var(--yellow); }
.state-orange { background: var(--orange-bg); color: var(--orange); } .state-orange { background: var(--orange-bg); color: var(--orange); }
.state-gray { background: var(--gray-bg); color: var(--gray); } .state-gray { background: var(--gray-bg); color: var(--gray); }
.state-text-green { color: var(--green); } .state-text-green { color: var(--accent-light); }
.state-text-red { color: var(--red); } .state-text-red { color: var(--red); }
.state-text-orange { color: var(--orange); } .state-text-orange { color: var(--orange); }
.state-text-yellow { color: var(--yellow); } .state-text-yellow { color: var(--yellow); }
@@ -421,7 +425,8 @@ h3 {
.stack-detail-actions { .stack-detail-actions {
display: flex; display: flex;
gap: .5rem; gap: .5rem;
margin-top: 1rem; margin-top: auto;
padding-top: 1rem;
flex-wrap: wrap; flex-wrap: wrap;
} }
@@ -454,11 +459,31 @@ h3 {
box-shadow: 0 4px 12px var(--accent-glow); box-shadow: 0 4px 12px var(--accent-glow);
} }
.btn-primary:hover { box-shadow: 0 6px 20px var(--accent-glow); } .btn-primary:hover { box-shadow: 0 6px 20px var(--accent-glow); }
.btn-success { background: var(--green); } .btn-success {
.btn-success:hover { box-shadow: 0 4px 12px rgba(35, 134, 54, 0.3); } background: linear-gradient(135deg, var(--accent-blue), var(--accent-light));
.btn-warning { background: var(--yellow); color: #0d1117; } box-shadow: 0 4px 12px var(--accent-glow);
.btn-danger { background: var(--red); } }
.btn-danger:hover { box-shadow: 0 4px 12px rgba(218, 54, 51, 0.3); } .btn-success:hover { box-shadow: 0 6px 20px var(--accent-glow); }
.btn-warning {
background: transparent;
border: 1px solid var(--border-color);
color: var(--text-secondary);
}
.btn-warning:hover {
border-color: var(--accent-blue);
color: var(--accent-light);
background: rgba(0, 136, 204, 0.08);
}
.btn-danger {
background: transparent;
border: 1px solid var(--border-color);
color: var(--text-secondary);
}
.btn-danger:hover {
border-color: var(--red);
color: var(--red);
background: var(--red-bg);
}
.btn-outline { .btn-outline {
background: transparent; background: transparent;
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
@@ -2483,14 +2508,16 @@ a.stat-card:hover {
margin-bottom: 0; margin-bottom: 0;
} }
.btn-danger { /* Red-filled danger buttons inside modal confirmation dialogs */
.modal-actions .btn-danger {
background: var(--red); background: var(--red);
color: white; color: white;
border-color: var(--red); border-color: var(--red);
} }
.modal-actions .btn-danger:hover {
.btn-danger:hover {
opacity: 0.85; opacity: 0.85;
background: var(--red);
color: white;
} }
/* Cross-drive backup card on deploy page */ /* Cross-drive backup card on deploy page */
@@ -2985,7 +3012,8 @@ a.stat-card:hover {
box-shadow: 0 0 0 2px rgba(218, 54, 51, 0.2); box-shadow: 0 0 0 2px rgba(218, 54, 51, 0.2);
} }
.btn-danger { /* Debug page: red-filled danger buttons for destructive debug actions */
.debug-section .btn-danger {
background: var(--red); background: var(--red);
color: #fff; color: #fff;
border: none; border: none;
@@ -2996,8 +3024,8 @@ a.stat-card:hover {
font-weight: 600; font-weight: 600;
transition: background .15s, opacity .15s; transition: background .15s, opacity .15s;
} }
.btn-danger:hover { background: #e5534b; } .debug-section .btn-danger:hover { background: #e5534b; }
.btn-danger:disabled { opacity: 0.5; cursor: not-allowed; } .debug-section .btn-danger:disabled { opacity: 0.5; cursor: not-allowed; }
.debug-spinner { .debug-spinner {
display: inline-block; display: inline-block;