needed for nested route rendering.
+ */
+@Component({
+ selector: 'app-console-admin-layout',
+ standalone: true,
+ imports: [RouterOutlet],
+ template: ` `,
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class ConsoleAdminLayoutComponent {}
diff --git a/src/Web/StellaOps.Web/src/app/features/console-admin/console-admin.routes.ts b/src/Web/StellaOps.Web/src/app/features/console-admin/console-admin.routes.ts
index 07da04d40..bb0a38217 100644
--- a/src/Web/StellaOps.Web/src/app/features/console-admin/console-admin.routes.ts
+++ b/src/Web/StellaOps.Web/src/app/features/console-admin/console-admin.routes.ts
@@ -13,6 +13,10 @@ export const consoleAdminRoutes: Routes = [
path: '',
canMatch: [requireAuthGuard],
data: { requiredScopes: [StellaOpsScopes.UI_ADMIN] },
+ loadComponent: () =>
+ import('./console-admin-layout.component').then(
+ (m) => m.ConsoleAdminLayoutComponent
+ ),
children: [
{
path: '',
diff --git a/src/Web/StellaOps.Web/src/app/features/console-admin/roles/roles-list.component.ts b/src/Web/StellaOps.Web/src/app/features/console-admin/roles/roles-list.component.ts
index 73cb2014e..00c82dcc8 100644
--- a/src/Web/StellaOps.Web/src/app/features/console-admin/roles/roles-list.component.ts
+++ b/src/Web/StellaOps.Web/src/app/features/console-admin/roles/roles-list.component.ts
@@ -212,8 +212,8 @@ interface RoleBundle {
.admin-header h1 {
margin: 0;
- font-size: 24px;
- font-weight: 600;
+ font-size: var(--font-size-2xl);
+ font-weight: var(--font-weight-semibold);
}
.tabs {
@@ -227,8 +227,8 @@ interface RoleBundle {
padding: 12px 24px;
border: none;
background: transparent;
- font-size: 14px;
- font-weight: 500;
+ font-size: var(--font-size-base);
+ font-weight: var(--font-weight-medium);
cursor: pointer;
border-bottom: 2px solid transparent;
margin-bottom: -2px;
@@ -250,7 +250,7 @@ interface RoleBundle {
.catalog-info {
background: var(--theme-bg-secondary);
padding: 16px;
- border-radius: 8px;
+ border-radius: var(--radius-lg);
margin-bottom: 16px;
border-left: 4px solid var(--theme-brand-primary);
}
@@ -264,8 +264,8 @@ interface RoleBundle {
max-width: 400px;
padding: 8px 12px;
border: 1px solid var(--theme-border-primary);
- border-radius: 4px;
- font-size: 14px;
+ border-radius: var(--radius-sm);
+ font-size: var(--font-size-base);
}
.module-group {
@@ -274,8 +274,8 @@ interface RoleBundle {
.module-group h3 {
margin: 0 0 12px 0;
- font-size: 18px;
- font-weight: 600;
+ font-size: var(--font-size-lg);
+ font-weight: var(--font-weight-semibold);
}
.bundle-grid {
@@ -287,7 +287,7 @@ interface RoleBundle {
.bundle-card {
background: var(--theme-bg-secondary);
border: 2px solid var(--theme-border-primary);
- border-radius: 8px;
+ border-radius: var(--radius-lg);
padding: 16px;
}
@@ -314,23 +314,23 @@ interface RoleBundle {
}
.bundle-name {
- font-weight: 600;
+ font-weight: var(--font-weight-semibold);
font-family: 'Monaco', 'Courier New', monospace;
- font-size: 13px;
+ font-size: var(--font-size-base);
}
.bundle-tier {
display: inline-block;
padding: 2px 8px;
- border-radius: 4px;
- font-size: 11px;
- font-weight: 500;
+ border-radius: var(--radius-sm);
+ font-size: var(--font-size-xs);
+ font-weight: var(--font-weight-medium);
text-transform: uppercase;
background: var(--theme-bg-tertiary);
}
.bundle-description {
- font-size: 13px;
+ font-size: var(--font-size-base);
color: var(--theme-text-secondary);
margin-bottom: 12px;
}
@@ -341,8 +341,8 @@ interface RoleBundle {
}
.scopes-header {
- font-size: 11px;
- font-weight: 600;
+ font-size: var(--font-size-xs);
+ font-weight: var(--font-weight-semibold);
text-transform: uppercase;
color: var(--theme-text-secondary);
margin-bottom: 8px;
@@ -353,23 +353,23 @@ interface RoleBundle {
padding: 4px 8px;
margin: 2px;
background: var(--theme-bg-tertiary);
- border-radius: 4px;
- font-size: 11px;
+ border-radius: var(--radius-sm);
+ font-size: var(--font-size-xs);
font-family: 'Monaco', 'Courier New', monospace;
}
.admin-form {
background: var(--theme-bg-secondary);
border: 1px solid var(--theme-border-primary);
- border-radius: 8px;
+ border-radius: var(--radius-lg);
padding: 24px;
margin-bottom: 24px;
}
.admin-form h2 {
margin: 0 0 16px 0;
- font-size: 18px;
- font-weight: 600;
+ font-size: var(--font-size-lg);
+ font-weight: var(--font-weight-semibold);
}
.form-group {
@@ -379,22 +379,22 @@ interface RoleBundle {
.form-group label {
display: block;
margin-bottom: 4px;
- font-weight: 500;
+ font-weight: var(--font-weight-medium);
}
.form-group input[type="text"] {
width: 100%;
padding: 8px 12px;
border: 1px solid var(--theme-border-primary);
- border-radius: 4px;
- font-size: 14px;
+ border-radius: var(--radius-sm);
+ font-size: var(--font-size-base);
}
.scope-selector {
max-height: 400px;
overflow-y: auto;
border: 1px solid var(--theme-border-primary);
- border-radius: 4px;
+ border-radius: var(--radius-sm);
padding: 12px;
}
@@ -403,7 +403,7 @@ interface RoleBundle {
align-items: center;
padding: 8px;
cursor: pointer;
- border-radius: 4px;
+ border-radius: var(--radius-sm);
}
.scope-checkbox:hover {
@@ -416,15 +416,15 @@ interface RoleBundle {
.scope-label {
flex: 1;
- font-weight: 500;
+ font-weight: var(--font-weight-medium);
}
.scope-checkbox code {
font-family: 'Monaco', 'Courier New', monospace;
- font-size: 12px;
+ font-size: var(--font-size-sm);
background: var(--theme-bg-tertiary);
padding: 2px 6px;
- border-radius: 2px;
+ border-radius: var(--radius-sm);
}
.form-actions {
@@ -437,7 +437,7 @@ interface RoleBundle {
width: 100%;
border-collapse: collapse;
background: var(--theme-bg-secondary);
- border-radius: 8px;
+ border-radius: var(--radius-lg);
overflow: hidden;
}
@@ -453,8 +453,8 @@ interface RoleBundle {
}
.admin-table th {
- font-weight: 600;
- font-size: 14px;
+ font-weight: var(--font-weight-semibold);
+ font-size: var(--font-size-base);
}
.admin-table tbody tr:hover {
@@ -472,8 +472,8 @@ interface RoleBundle {
padding: 2px 8px;
background: var(--theme-brand-primary);
color: var(--color-text-heading);
- border-radius: 4px;
- font-size: 12px;
+ border-radius: var(--radius-sm);
+ font-size: var(--font-size-sm);
}
.action-buttons {
@@ -486,9 +486,9 @@ interface RoleBundle {
.btn-sm {
padding: 8px 16px;
border: none;
- border-radius: 4px;
- font-size: 14px;
- font-weight: 500;
+ border-radius: var(--radius-sm);
+ font-size: var(--font-size-base);
+ font-weight: var(--font-weight-medium);
cursor: pointer;
}
@@ -518,7 +518,7 @@ interface RoleBundle {
.btn-sm {
padding: 4px 12px;
- font-size: 12px;
+ font-size: var(--font-size-sm);
background: var(--theme-bg-tertiary);
color: var(--theme-text-primary);
}
@@ -534,7 +534,7 @@ interface RoleBundle {
.alert {
padding: 12px 16px;
- border-radius: 4px;
+ border-radius: var(--radius-sm);
margin-bottom: 16px;
}
@@ -552,7 +552,7 @@ interface RoleBundle {
code {
font-family: 'Monaco', 'Courier New', monospace;
- font-size: 12px;
+ font-size: var(--font-size-sm);
}
`]
})
diff --git a/src/Web/StellaOps.Web/src/app/features/console-admin/tenants/tenants-list.component.ts b/src/Web/StellaOps.Web/src/app/features/console-admin/tenants/tenants-list.component.ts
index 028b1e03a..88e3e80ee 100644
--- a/src/Web/StellaOps.Web/src/app/features/console-admin/tenants/tenants-list.component.ts
+++ b/src/Web/StellaOps.Web/src/app/features/console-admin/tenants/tenants-list.component.ts
@@ -84,15 +84,15 @@ import { FreshAuthService } from '../../../core/auth/fresh-auth.service';
.admin-header h1 {
margin: 0;
- font-size: 24px;
- font-weight: 600;
+ font-size: var(--font-size-2xl);
+ font-weight: var(--font-weight-semibold);
}
.admin-table {
width: 100%;
border-collapse: collapse;
background: white;
- border-radius: 8px;
+ border-radius: var(--radius-lg);
overflow: hidden;
}
@@ -105,25 +105,25 @@ import { FreshAuthService } from '../../../core/auth/fresh-auth.service';
.admin-table th {
background: var(--color-surface-secondary);
- font-weight: 600;
+ font-weight: var(--font-weight-semibold);
color: var(--color-text-secondary);
}
.status-badge {
padding: 4px 8px;
- border-radius: 4px;
- font-size: 12px;
- font-weight: 500;
+ border-radius: var(--radius-sm);
+ font-size: var(--font-size-sm);
+ font-weight: var(--font-weight-medium);
}
.status-active {
- background: #dcfce7;
- color: #166534;
+ background: var(--color-status-success-bg);
+ color: var(--color-status-success-text);
}
.status-suspended {
- background: #fee2e2;
- color: #991b1b;
+ background: var(--color-status-error-bg);
+ color: var(--color-status-error-text);
}
.loading,
@@ -135,7 +135,7 @@ import { FreshAuthService } from '../../../core/auth/fresh-auth.service';
}
.error {
- color: #dc2626;
+ color: var(--color-status-error);
}
`]
})
diff --git a/src/Web/StellaOps.Web/src/app/features/console-admin/tokens/tokens-list.component.ts b/src/Web/StellaOps.Web/src/app/features/console-admin/tokens/tokens-list.component.ts
index 541d1769e..1f1ee6df7 100644
--- a/src/Web/StellaOps.Web/src/app/features/console-admin/tokens/tokens-list.component.ts
+++ b/src/Web/StellaOps.Web/src/app/features/console-admin/tokens/tokens-list.component.ts
@@ -151,8 +151,8 @@ import { StellaOpsScopes } from '../../../core/auth/scopes';
.admin-header h1 {
margin: 0;
- font-size: 24px;
- font-weight: 600;
+ font-size: var(--font-size-2xl);
+ font-weight: var(--font-weight-semibold);
}
.header-actions {
@@ -168,8 +168,8 @@ import { StellaOpsScopes } from '../../../core/auth/scopes';
.filter-select {
padding: 8px 12px;
border: 1px solid var(--theme-border-primary);
- border-radius: 4px;
- font-size: 14px;
+ border-radius: var(--radius-sm);
+ font-size: var(--font-size-base);
background: var(--theme-bg-secondary);
cursor: pointer;
}
@@ -184,19 +184,19 @@ import { StellaOpsScopes } from '../../../core/auth/scopes';
.summary-card {
background: var(--theme-bg-secondary);
border: 1px solid var(--theme-border-primary);
- border-radius: 8px;
+ border-radius: var(--radius-lg);
padding: 16px;
}
.summary-label {
- font-size: 13px;
+ font-size: var(--font-size-base);
color: var(--theme-text-secondary);
margin-bottom: 8px;
}
.summary-value {
- font-size: 28px;
- font-weight: 600;
+ font-size: var(--font-size-3xl);
+ font-weight: var(--font-weight-semibold);
color: var(--theme-brand-primary);
}
@@ -204,7 +204,7 @@ import { StellaOpsScopes } from '../../../core/auth/scopes';
width: 100%;
border-collapse: collapse;
background: var(--theme-bg-secondary);
- border-radius: 8px;
+ border-radius: var(--radius-lg);
overflow: hidden;
margin-bottom: 24px;
}
@@ -218,12 +218,12 @@ import { StellaOpsScopes } from '../../../core/auth/scopes';
padding: 12px;
text-align: left;
border-bottom: 1px solid var(--theme-border-primary);
- font-size: 13px;
+ font-size: var(--font-size-base);
}
.admin-table th {
- font-weight: 600;
- font-size: 12px;
+ font-weight: var(--font-weight-semibold);
+ font-size: var(--font-size-sm);
text-transform: uppercase;
color: var(--theme-text-secondary);
}
@@ -242,18 +242,18 @@ import { StellaOpsScopes } from '../../../core/auth/scopes';
.token-id {
font-family: 'Monaco', 'Courier New', monospace;
- font-size: 11px;
+ font-size: var(--font-size-xs);
background: var(--theme-bg-tertiary);
padding: 2px 4px;
- border-radius: 2px;
+ border-radius: var(--radius-sm);
}
.type-badge {
display: inline-block;
padding: 4px 8px;
- border-radius: 4px;
- font-size: 11px;
- font-weight: 500;
+ border-radius: var(--radius-sm);
+ font-size: var(--font-size-xs);
+ font-weight: var(--font-weight-medium);
text-transform: uppercase;
}
@@ -270,9 +270,9 @@ import { StellaOpsScopes } from '../../../core/auth/scopes';
.status-badge {
display: inline-block;
padding: 4px 8px;
- border-radius: 4px;
- font-size: 12px;
- font-weight: 500;
+ border-radius: var(--radius-sm);
+ font-size: var(--font-size-sm);
+ font-weight: var(--font-weight-medium);
}
.status-badge.status-active {
@@ -297,12 +297,12 @@ import { StellaOpsScopes } from '../../../core/auth/scopes';
.btn-sm {
padding: 4px 12px;
- font-size: 12px;
+ font-size: var(--font-size-sm);
background: var(--theme-bg-tertiary);
color: var(--theme-text-primary);
border: none;
- border-radius: 4px;
- font-weight: 500;
+ border-radius: var(--radius-sm);
+ font-weight: var(--font-weight-medium);
cursor: pointer;
}
@@ -321,15 +321,15 @@ import { StellaOpsScopes } from '../../../core/auth/scopes';
padding: 16px;
background: var(--theme-bg-secondary);
border: 1px solid var(--theme-border-primary);
- border-radius: 8px;
+ border-radius: var(--radius-lg);
}
.btn-danger {
padding: 8px 16px;
border: none;
- border-radius: 4px;
- font-size: 14px;
- font-weight: 500;
+ border-radius: var(--radius-sm);
+ font-size: var(--font-size-base);
+ font-weight: var(--font-weight-medium);
cursor: pointer;
background: var(--theme-status-error);
color: white;
@@ -347,7 +347,7 @@ import { StellaOpsScopes } from '../../../core/auth/scopes';
.alert {
padding: 12px 16px;
- border-radius: 4px;
+ border-radius: var(--radius-sm);
margin-bottom: 16px;
}
@@ -365,7 +365,7 @@ import { StellaOpsScopes } from '../../../core/auth/scopes';
code {
font-family: 'Monaco', 'Courier New', monospace;
- font-size: 12px;
+ font-size: var(--font-size-sm);
}
`]
})
diff --git a/src/Web/StellaOps.Web/src/app/features/console-admin/users/users-list.component.ts b/src/Web/StellaOps.Web/src/app/features/console-admin/users/users-list.component.ts
index 9fad84fb4..049a0183a 100644
--- a/src/Web/StellaOps.Web/src/app/features/console-admin/users/users-list.component.ts
+++ b/src/Web/StellaOps.Web/src/app/features/console-admin/users/users-list.component.ts
@@ -160,22 +160,22 @@ import { StellaOpsScopes } from '../../../core/auth/scopes';
.admin-header h1 {
margin: 0;
- font-size: 24px;
- font-weight: 600;
+ font-size: var(--font-size-2xl);
+ font-weight: var(--font-weight-semibold);
}
.admin-form {
background: var(--theme-bg-secondary);
border: 1px solid var(--theme-border-primary);
- border-radius: 8px;
+ border-radius: var(--radius-lg);
padding: 24px;
margin-bottom: 24px;
}
.admin-form h2 {
margin: 0 0 16px 0;
- font-size: 18px;
- font-weight: 600;
+ font-size: var(--font-size-lg);
+ font-weight: var(--font-weight-semibold);
}
.form-group {
@@ -185,15 +185,15 @@ import { StellaOpsScopes } from '../../../core/auth/scopes';
.form-group label {
display: block;
margin-bottom: 4px;
- font-weight: 500;
+ font-weight: var(--font-weight-medium);
}
.form-group input {
width: 100%;
padding: 8px 12px;
border: 1px solid var(--theme-border-primary);
- border-radius: 4px;
- font-size: 14px;
+ border-radius: var(--radius-sm);
+ font-size: var(--font-size-base);
}
.form-group input:disabled {
@@ -211,7 +211,7 @@ import { StellaOpsScopes } from '../../../core/auth/scopes';
width: 100%;
border-collapse: collapse;
background: var(--theme-bg-secondary);
- border-radius: 8px;
+ border-radius: var(--radius-lg);
overflow: hidden;
}
@@ -227,8 +227,8 @@ import { StellaOpsScopes } from '../../../core/auth/scopes';
}
.admin-table th {
- font-weight: 600;
- font-size: 14px;
+ font-weight: var(--font-weight-semibold);
+ font-size: var(--font-size-base);
}
.admin-table tbody tr:hover {
@@ -250,16 +250,16 @@ import { StellaOpsScopes } from '../../../core/auth/scopes';
padding: 2px 8px;
background: var(--theme-brand-primary);
color: var(--color-text-heading);
- border-radius: 4px;
- font-size: 12px;
+ border-radius: var(--radius-sm);
+ font-size: var(--font-size-sm);
}
.status-badge {
display: inline-block;
padding: 4px 8px;
- border-radius: 4px;
- font-size: 12px;
- font-weight: 500;
+ border-radius: var(--radius-sm);
+ font-size: var(--font-size-sm);
+ font-weight: var(--font-weight-medium);
}
.status-badge.status-active {
@@ -282,9 +282,9 @@ import { StellaOpsScopes } from '../../../core/auth/scopes';
.btn-sm {
padding: 8px 16px;
border: none;
- border-radius: 4px;
- font-size: 14px;
- font-weight: 500;
+ border-radius: var(--radius-sm);
+ font-size: var(--font-size-base);
+ font-weight: var(--font-weight-medium);
cursor: pointer;
}
@@ -314,7 +314,7 @@ import { StellaOpsScopes } from '../../../core/auth/scopes';
.btn-sm {
padding: 4px 12px;
- font-size: 12px;
+ font-size: var(--font-size-sm);
background: var(--theme-bg-tertiary);
color: var(--theme-text-primary);
}
@@ -335,7 +335,7 @@ import { StellaOpsScopes } from '../../../core/auth/scopes';
.alert {
padding: 12px 16px;
- border-radius: 4px;
+ border-radius: var(--radius-sm);
margin-bottom: 16px;
}
@@ -353,10 +353,10 @@ import { StellaOpsScopes } from '../../../core/auth/scopes';
code {
font-family: 'Monaco', 'Courier New', monospace;
- font-size: 12px;
+ font-size: var(--font-size-sm);
background: var(--theme-bg-tertiary);
padding: 2px 4px;
- border-radius: 2px;
+ border-radius: var(--radius-sm);
}
`]
})
diff --git a/src/Web/StellaOps.Web/src/app/features/control-plane/control-plane-dashboard.component.ts b/src/Web/StellaOps.Web/src/app/features/control-plane/control-plane-dashboard.component.ts
index b3871f12e..e2b49e07e 100644
--- a/src/Web/StellaOps.Web/src/app/features/control-plane/control-plane-dashboard.component.ts
+++ b/src/Web/StellaOps.Web/src/app/features/control-plane/control-plane-dashboard.component.ts
@@ -4,6 +4,7 @@ import {
OnInit,
inject,
signal,
+ computed,
} from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterLink } from '@angular/router';
@@ -19,20 +20,27 @@ import type {
ActiveDeployment,
RecentRelease,
} from '../../core/api/release-dashboard.models';
+import { LoadingStateComponent } from '../../shared/components/loading-state/loading-state.component';
/**
* ControlPlaneDashboardComponent - Main landing page for the new shell.
*
* Displays:
+ * - Hero/welcome header (always visible, even on error)
* - Environment pipeline overview
* - Action inbox (pending approvals + active deployments)
* - Pending promotions table (recent releases)
+ *
+ * Graceful degradation: when the API is unreachable the hero section
+ * still renders, skeleton placeholders appear for data areas, and
+ * a secondary error banner offers a retry action.
*/
@Component({
selector: 'app-control-plane-dashboard',
- imports: [CommonModule, RouterLink],
+ imports: [CommonModule, RouterLink, LoadingStateComponent],
template: `
+
+
+ @if (error()) {
+
+
+
+
+
+
+
+
+ Unable to load live data
+ {{ error() }}
+
+
+
Retry
+
+ }
+
@if (loading()) {
-
-
-
Loading dashboard data…
-
- } @else if (error()) {
-
-
Failed to load dashboard
-
{{ error() }}
-
Retry
-
- } @else {
+
+ } @else if (hasData()) {
Environment Pipeline
@@ -111,7 +130,7 @@ import type {
{{ approval.urgency }}
- {{ approval.sourceEnvironment }} → {{ approval.targetEnvironment }}
+ {{ approval.sourceEnvironment }} → {{ approval.targetEnvironment }}
}
@@ -177,7 +196,7 @@ import type {
{{ release.status }}
- {{ release.currentEnvironment ?? '—' }}
+ {{ release.currentEnvironment ?? '—' }}
{{ release.componentCount }}
@if (release.status === 'ready' || release.status === 'promoting') {
@@ -193,33 +212,83 @@ import type {
}
+ } @else if (error()) {
+
+
+ Environment Pipeline
+
+ @for (label of skeletonEnvLabels; track label) {
+
+ @if (label !== 'Production') {
+
+ }
+ }
+
+
+
+
+
+ Pending Approvals
+ Promotions awaiting review
+
+
+
+
+
+ Active Deployments
+ Currently in progress
+
+
+
+
+
+
}
`,
styles: [`
:host {
- --so-brand: #F5A623;
- --so-brand-hover: #E09115;
- --so-accent: #D4920A;
- --so-accent-muted: #C4820A;
- --so-brand-soft: #FEF3E2;
- --so-surface: #FFFCF5;
- --so-surface-elevated: #fff;
- --so-base: #FFF9ED;
- --so-heading: #1C1200;
- --so-text: #3D2E0A;
- --so-text-secondary: #6B5A2E;
- --so-mute: #D4C9A8;
+ --so-brand: var(--color-brand-primary);
+ --so-brand-hover: var(--color-brand-primary);
+ --so-accent: var(--color-brand-secondary);
+ --so-accent-muted: var(--color-brand-secondary);
+ --so-brand-soft: var(--color-brand-soft);
+ --so-surface: var(--color-surface-secondary);
+ --so-surface-elevated: var(--color-surface-primary);
+ --so-base: var(--color-surface-tertiary);
+ --so-heading: var(--color-surface-inverse);
+ --so-text: var(--color-text-heading);
+ --so-text-secondary: var(--color-text-secondary);
+ --so-mute: var(--color-border-secondary);
--so-border-light: hsla(45,34%,75%,.3);
--so-border-medium: hsla(45,34%,75%,.5);
--so-border-emphasis: rgba(245,166,35,.4);
- --so-success: #059669;
- --so-success-soft: #D1FAE5;
- --so-warning: #D97706;
+ --so-success: var(--color-status-success-text);
+ --so-success-soft: var(--color-status-success-bg);
+ --so-warning: var(--color-status-warning-text);
--so-warning-soft: rgba(217,119,6,.08);
- --so-error: #DC2626;
+ --so-error: var(--color-status-error);
--so-error-soft: rgba(220,38,38,.08);
- --so-info: #2563EB;
+ --so-info: var(--color-status-info-text);
--so-info-soft: rgba(37,99,235,.08);
--so-shadow-brand: rgba(245,166,35,.15);
--so-shadow-dark: rgba(28,18,0,.08);
@@ -256,7 +325,7 @@ import type {
padding: 28px 32px;
background: var(--so-surface-elevated);
border: 1px solid var(--so-border-light);
- border-radius: 16px;
+ border-radius: var(--radius-2xl);
box-shadow: var(--so-shadow-sm);
position: relative;
overflow: hidden;
@@ -274,8 +343,8 @@ import type {
.dashboard__title {
margin: 0 0 6px;
- font-size: 26px;
- font-weight: 800;
+ font-size: var(--font-size-3xl);
+ font-weight: var(--font-weight-bold);
color: var(--so-heading);
letter-spacing: -0.035em;
}
@@ -283,7 +352,7 @@ import type {
.dashboard__subtitle {
margin: 0;
color: var(--so-text-secondary);
- font-size: 14px;
+ font-size: var(--font-size-base);
line-height: 1.5;
}
@@ -300,8 +369,8 @@ import type {
.dashboard__section-title {
margin: 0 0 14px;
- font-size: 17px;
- font-weight: 700;
+ font-size: var(--font-size-md);
+ font-weight: var(--font-weight-bold);
color: var(--so-heading);
letter-spacing: -0.02em;
}
@@ -313,52 +382,103 @@ import type {
margin-bottom: 28px;
}
- /* Loading */
- .dashboard__loading {
+ /* Error Banner (secondary, non-blocking) */
+ .dashboard__error-banner {
display: flex;
- flex-direction: column;
align-items: center;
- justify-content: center;
- min-height: 300px;
+ justify-content: space-between;
gap: 16px;
- color: var(--so-text-secondary);
- }
-
- .dashboard__spinner {
- width: 36px;
- height: 36px;
- border: 3px solid var(--so-border-light);
- border-top-color: var(--so-brand);
- border-radius: 50%;
- animation: spin 0.8s linear infinite;
- }
-
- /* Error */
- .dashboard__error {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- min-height: 200px;
- gap: 10px;
- padding: 40px;
- background: var(--so-surface-elevated);
+ margin-bottom: 20px;
+ padding: 14px 20px;
+ background: var(--so-error-soft);
border: 1px solid rgba(220,38,38,.2);
- border-radius: 16px;
- text-align: center;
- box-shadow: var(--so-shadow-sm);
+ border-radius: var(--radius-xl);
+ animation: banner-in 300ms var(--so-ease-out) both;
}
- .dashboard__error-title {
- margin: 0;
- font-weight: 700;
+ @keyframes banner-in {
+ from { opacity: 0; transform: translateY(-8px); }
+ to { opacity: 1; transform: translateY(0); }
+ }
+
+ .dashboard__error-banner-content {
+ display: flex;
+ align-items: flex-start;
+ gap: 12px;
+ min-width: 0;
+ }
+
+ .dashboard__error-banner-icon {
+ flex-shrink: 0;
+ color: var(--so-error);
+ margin-top: 1px;
+ }
+
+ .dashboard__error-banner-title {
+ display: block;
+ font-size: var(--font-size-base);
+ font-weight: var(--font-weight-bold);
color: var(--so-heading);
}
- .dashboard__error-message {
- margin: 0;
+ .dashboard__error-banner-detail {
+ display: block;
+ font-size: var(--font-size-sm);
color: var(--so-text-secondary);
- font-size: 13px;
+ margin-top: 2px;
+ word-break: break-word;
+ }
+
+ /* Skeleton placeholders */
+ .pipeline--skeleton {
+ opacity: 0.6;
+ }
+
+ .pipeline__stage--skeleton {
+ pointer-events: none;
+ }
+
+ .skeleton-text {
+ color: var(--so-mute);
+ }
+
+ .skeleton-text--name {
+ display: block;
+ font-weight: var(--font-weight-bold);
+ font-size: var(--font-size-base);
+ }
+
+ .skeleton-text--count {
+ display: block;
+ font-size: var(--font-size-sm);
+ margin-top: 2px;
+ }
+
+ .card--skeleton {
+ opacity: 0.6;
+ pointer-events: none;
+ }
+
+ .skeleton-bar {
+ height: 14px;
+ background: var(--so-border-light);
+ border-radius: var(--radius-lg);
+ margin-bottom: 12px;
+ animation: skeleton-pulse 1.5s ease-in-out infinite;
+ }
+
+ .skeleton-bar--short {
+ width: 60%;
+ }
+
+ .table-container--skeleton {
+ padding: 24px;
+ opacity: 0.6;
+ }
+
+ @keyframes skeleton-pulse {
+ 0%, 100% { opacity: 0.5; }
+ 50% { opacity: 1; }
}
.dashboard__empty {
@@ -367,8 +487,8 @@ import type {
color: var(--so-text-secondary);
background: var(--so-surface-elevated);
border: 2px dashed var(--so-mute);
- border-radius: 16px;
- font-size: 14px;
+ border-radius: var(--radius-2xl);
+ font-size: var(--font-size-base);
}
/* Pipeline */
@@ -379,7 +499,7 @@ import type {
padding: 20px 24px;
background: var(--so-surface-elevated);
border: 1px solid var(--so-border-light);
- border-radius: 16px;
+ border-radius: var(--radius-2xl);
box-shadow: var(--so-shadow-sm);
overflow-x: auto;
}
@@ -390,7 +510,7 @@ import type {
padding: 16px;
background: var(--so-surface);
border: 1px solid var(--so-border-light);
- border-radius: 12px;
+ border-radius: var(--radius-xl);
text-align: center;
transition: all 220ms var(--so-ease-out);
position: relative;
@@ -417,14 +537,14 @@ import type {
.pipeline__stage-name {
display: block;
- font-weight: 700;
- font-size: 14px;
+ font-weight: var(--font-weight-bold);
+ font-size: var(--font-size-base);
color: var(--so-heading);
letter-spacing: -0.01em;
}
.pipeline__stage-count {
- font-size: 12px;
+ font-size: var(--font-size-sm);
color: var(--so-text-secondary);
margin-top: 2px;
display: block;
@@ -433,9 +553,9 @@ import type {
.pipeline__health-badge {
display: inline-block;
padding: 3px 10px;
- font-size: 10px;
- font-weight: 700;
- border-radius: 6px;
+ font-size: var(--font-size-xs);
+ font-weight: var(--font-weight-bold);
+ border-radius: var(--radius-md);
text-transform: uppercase;
letter-spacing: 0.06em;
}
@@ -467,8 +587,8 @@ import type {
.pipeline__pending-count {
display: block;
margin-top: 6px;
- font-size: 11px;
- font-weight: 600;
+ font-size: var(--font-size-xs);
+ font-weight: var(--font-weight-semibold);
color: var(--so-warning);
}
@@ -483,7 +603,7 @@ import type {
padding: 24px;
background: var(--so-surface-elevated);
border: 1px solid var(--so-border-light);
- border-radius: 16px;
+ border-radius: var(--radius-2xl);
box-shadow: var(--so-shadow-sm);
transition: box-shadow 220ms var(--so-ease-out), transform 220ms var(--so-ease-out);
}
@@ -495,21 +615,21 @@ import type {
.card__title {
margin: 0 0 4px;
- font-size: 16px;
- font-weight: 700;
+ font-size: var(--font-size-md);
+ font-weight: var(--font-weight-bold);
color: var(--so-heading);
letter-spacing: -0.01em;
}
.card__subtitle {
margin: 0 0 16px;
- font-size: 13px;
+ font-size: var(--font-size-base);
color: var(--so-text-secondary);
}
.card__empty {
margin: 0 0 16px;
- font-size: 13px;
+ font-size: var(--font-size-base);
color: var(--so-text-secondary);
font-style: italic;
}
@@ -537,8 +657,8 @@ import type {
}
.card__item-link {
- font-weight: 600;
- font-size: 13px;
+ font-weight: var(--font-weight-semibold);
+ font-size: var(--font-size-base);
color: var(--so-accent);
text-decoration: none;
transition: color 150ms var(--so-ease-out);
@@ -551,16 +671,16 @@ import type {
.card__item-detail {
display: block;
- font-size: 12px;
+ font-size: var(--font-size-sm);
color: var(--so-text-secondary);
margin-top: 4px;
}
.card__urgency {
- font-size: 10px;
- font-weight: 700;
+ font-size: var(--font-size-xs);
+ font-weight: var(--font-weight-bold);
padding: 2px 8px;
- border-radius: 6px;
+ border-radius: var(--radius-md);
text-transform: uppercase;
letter-spacing: 0.04em;
}
@@ -585,10 +705,10 @@ import type {
}
.card__dep-status {
- font-size: 10px;
- font-weight: 700;
+ font-size: var(--font-size-xs);
+ font-weight: var(--font-weight-bold);
padding: 2px 8px;
- border-radius: 6px;
+ border-radius: var(--radius-md);
text-transform: uppercase;
letter-spacing: 0.04em;
}
@@ -609,7 +729,7 @@ import type {
.card__progress {
height: 5px;
background: var(--so-border-light);
- border-radius: 3px;
+ border-radius: var(--radius-sm);
margin: 6px 0;
overflow: hidden;
}
@@ -617,7 +737,7 @@ import type {
.card__progress-bar {
height: 100%;
background: var(--so-gradient-cta);
- border-radius: 3px;
+ border-radius: var(--radius-sm);
transition: width 400ms var(--so-ease-out);
}
@@ -631,7 +751,7 @@ import type {
overflow-x: auto;
background: var(--so-surface-elevated);
border: 1px solid var(--so-border-light);
- border-radius: 16px;
+ border-radius: var(--radius-2xl);
box-shadow: var(--so-shadow-sm);
}
@@ -648,19 +768,19 @@ import type {
}
.table th {
- font-size: 11px;
- font-weight: 700;
+ font-size: var(--font-size-xs);
+ font-weight: var(--font-weight-bold);
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--so-text-secondary);
background: var(--so-surface);
}
- .table th:first-child { border-radius: 16px 0 0 0; }
+ .table th:first-child { border-radius: var(--radius-2xl) 0 0 0; }
.table th:last-child { border-radius: 0 16px 0 0; }
.table td {
- font-size: 13px;
+ font-size: var(--font-size-base);
color: var(--so-text);
}
@@ -679,7 +799,7 @@ import type {
.table a {
color: var(--so-accent);
text-decoration: none;
- font-weight: 600;
+ font-weight: var(--font-weight-semibold);
}
.table a:hover {
@@ -691,9 +811,9 @@ import type {
.badge {
display: inline-block;
padding: 3px 10px;
- font-size: 10px;
- font-weight: 700;
- border-radius: 6px;
+ font-size: var(--font-size-xs);
+ font-weight: var(--font-weight-bold);
+ border-radius: var(--radius-md);
text-transform: uppercase;
letter-spacing: 0.04em;
}
@@ -737,9 +857,9 @@ import type {
gap: 6px;
padding: 10px 20px;
border: none;
- border-radius: 10px;
- font-size: 13px;
- font-weight: 700;
+ border-radius: var(--radius-xl);
+ font-size: var(--font-size-base);
+ font-weight: var(--font-weight-bold);
text-decoration: none;
cursor: pointer;
transition: all 200ms var(--so-ease-out);
@@ -771,8 +891,8 @@ import type {
.btn--small {
padding: 5px 12px;
- font-size: 12px;
- border-radius: 8px;
+ font-size: var(--font-size-sm);
+ border-radius: var(--radius-lg);
}
@keyframes spin {
@@ -780,10 +900,11 @@ import type {
}
@media (prefers-reduced-motion: reduce) {
- .dashboard__spinner { animation: none; border-top-color: transparent; }
.btn, .card, .pipeline__stage, .table tbody tr { transition: none; }
.card__progress-bar { transition: none; }
.dashboard { animation: none; }
+ .dashboard__error-banner { animation: none; }
+ .skeleton-bar { animation: none; }
}
@media (max-width: 768px) {
@@ -792,6 +913,12 @@ import type {
padding: 20px;
}
+ .dashboard__error-banner {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 12px;
+ }
+
.pipeline {
flex-direction: column;
padding: 16px;
@@ -814,11 +941,18 @@ export class ControlPlaneDashboardComponent implements OnInit {
readonly loading = signal(true);
readonly error = signal(null);
+ readonly dataLoaded = signal(false);
readonly environments = signal([]);
readonly pendingApprovals = signal([]);
readonly activeDeployments = signal([]);
readonly recentReleases = signal([]);
+ /** True when at least one successful data load has occurred. */
+ readonly hasData = computed(() => this.dataLoaded());
+
+ /** Placeholder environment labels for skeleton state. */
+ readonly skeletonEnvLabels = ['Development', 'Staging', 'UAT', 'Production'];
+
ngOnInit(): void {
this.loadData();
}
@@ -835,6 +969,7 @@ export class ControlPlaneDashboardComponent implements OnInit {
this.pendingApprovals.set(data.pendingApprovals);
this.activeDeployments.set(data.activeDeployments);
this.recentReleases.set(data.recentReleases);
+ this.dataLoaded.set(true);
this.loading.set(false);
},
error: (err: unknown) => {
diff --git a/src/Web/StellaOps.Web/src/app/features/dashboard/ai-risk-drivers.component.ts b/src/Web/StellaOps.Web/src/app/features/dashboard/ai-risk-drivers.component.ts
index 0fe6c521b..5d38f6d47 100644
--- a/src/Web/StellaOps.Web/src/app/features/dashboard/ai-risk-drivers.component.ts
+++ b/src/Web/StellaOps.Web/src/app/features/dashboard/ai-risk-drivers.component.ts
@@ -198,9 +198,9 @@ export interface DashboardAiData {
.ai-risk-drivers__section {
padding: 1rem;
- background: white;
- border: 1px solid #e5e7eb;
- border-radius: 8px;
+ background: var(--color-surface-primary);
+ border: 1px solid var(--color-border-primary);
+ border-radius: var(--radius-lg);
}
.ai-risk-drivers__section-header {
@@ -213,17 +213,17 @@ export interface DashboardAiData {
.ai-risk-drivers__section-title {
margin: 0;
font-size: 0.9375rem;
- font-weight: 600;
- color: #111827;
+ font-weight: var(--font-weight-semibold);
+ color: var(--color-text-heading);
}
.ai-risk-drivers__deterministic-badge {
padding: 0.125rem 0.5rem;
- background: #dbeafe;
- border-radius: 9999px;
+ background: var(--color-status-info-bg);
+ border-radius: var(--radius-full);
font-size: 0.6875rem;
- font-weight: 500;
- color: #1e40af;
+ font-weight: var(--font-weight-medium);
+ color: var(--color-status-info-text);
}
.ai-risk-drivers__list {
@@ -240,14 +240,14 @@ export interface DashboardAiData {
align-items: center;
gap: 0.75rem;
padding: 0.75rem;
- background: #f9fafb;
- border-radius: 6px;
+ background: var(--color-surface-secondary);
+ border-radius: var(--radius-md);
cursor: pointer;
transition: background 0.15s ease;
}
.ai-risk-drivers__item:hover {
- background: #f3f4f6;
+ background: var(--color-surface-tertiary);
}
.ai-risk-drivers__rank {
@@ -257,15 +257,15 @@ export interface DashboardAiData {
width: 1.5rem;
height: 1.5rem;
background: var(--color-brand-primary);
- border-radius: 50%;
+ border-radius: var(--radius-full);
font-size: 0.75rem;
- font-weight: 600;
+ font-weight: var(--font-weight-semibold);
color: var(--color-text-heading);
flex-shrink: 0;
}
.ai-risk-drivers__item--bottleneck .ai-risk-drivers__rank {
- background: #f59e0b;
+ background: var(--color-severity-medium);
}
.ai-risk-drivers__content {
@@ -277,21 +277,21 @@ export interface DashboardAiData {
.ai-risk-drivers__description {
font-size: 0.875rem;
- color: #111827;
+ color: var(--color-text-heading);
}
.ai-risk-drivers__stats,
.ai-risk-drivers__percentage {
font-size: 0.75rem;
- color: #6b7280;
+ color: var(--color-text-muted);
}
.ai-risk-drivers__evidence-link,
.ai-risk-drivers__action {
padding: 0.375rem 0.75rem;
background: transparent;
- border: 1px solid #d1d5db;
- border-radius: 4px;
+ border: 1px solid var(--color-border-primary);
+ border-radius: var(--radius-sm);
font-size: 0.75rem;
color: var(--color-brand-primary);
cursor: pointer;
@@ -301,14 +301,14 @@ export interface DashboardAiData {
.ai-risk-drivers__evidence-link:hover,
.ai-risk-drivers__action:hover {
- background: #eef2ff;
- border-color: #FFCF70;
+ background: var(--color-brand-light);
+ border-color: var(--color-brand-primary);
}
.ai-risk-drivers__empty {
padding: 1rem;
text-align: center;
- color: #9ca3af;
+ color: var(--color-text-muted);
font-size: 0.875rem;
font-style: italic;
}
@@ -324,34 +324,34 @@ export interface DashboardAiData {
justify-content: space-between;
align-items: center;
padding: 0.5rem;
- background: #f9fafb;
- border-radius: 4px;
+ background: var(--color-surface-secondary);
+ border-radius: var(--radius-sm);
}
.ai-risk-drivers__trend-label {
font-size: 0.8125rem;
- color: #6b7280;
+ color: var(--color-text-muted);
}
.ai-risk-drivers__trend-value {
font-size: 0.875rem;
- font-weight: 500;
+ font-weight: var(--font-weight-medium);
}
.ai-risk-drivers__trend-value--increasing {
- color: #dc2626;
+ color: var(--color-status-error);
}
.ai-risk-drivers__trend-value--decreasing {
- color: #16a34a;
+ color: var(--color-status-success);
}
.ai-risk-drivers__trend-value--stable {
- color: #6b7280;
+ color: var(--color-text-muted);
}
.ai-risk-drivers__trend-value--positive {
- color: #16a34a;
+ color: var(--color-status-success);
}
`]
})
diff --git a/src/Web/StellaOps.Web/src/app/features/deadletter/deadletter-dashboard.component.ts b/src/Web/StellaOps.Web/src/app/features/deadletter/deadletter-dashboard.component.ts
index 4f3153f05..2d806bbea 100644
--- a/src/Web/StellaOps.Web/src/app/features/deadletter/deadletter-dashboard.component.ts
+++ b/src/Web/StellaOps.Web/src/app/features/deadletter/deadletter-dashboard.component.ts
@@ -424,7 +424,7 @@ import {
.subtitle {
margin: 0.25rem 0 0;
- color: var(--text-secondary);
+ color: var(--color-text-secondary);
}
.header-actions {
@@ -437,14 +437,14 @@ import {
align-items: center;
gap: 0.5rem;
padding: 0.5rem 1rem;
- border: 1px solid var(--border-color);
- border-radius: 6px;
- background: var(--bg-primary);
+ border: 1px solid var(--color-border-primary);
+ border-radius: var(--radius-md);
+ background: var(--color-surface-primary);
cursor: pointer;
font-size: 0.875rem;
}
- .btn:hover:not(:disabled) { background: var(--bg-tertiary); }
+ .btn:hover:not(:disabled) { background: var(--color-surface-tertiary); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
.btn-primary { background: var(--color-primary); color: var(--color-text-heading); border-color: var(--color-primary); }
.btn-sm { padding: 0.25rem 0.5rem; font-size: 0.75rem; }
@@ -460,13 +460,13 @@ import {
padding: 1rem;
background: var(--color-primary-bg);
border: 1px solid var(--color-primary);
- border-radius: 8px;
+ border-radius: var(--radius-lg);
margin-bottom: 1.5rem;
}
.progress-info { flex: 1; }
- .progress-info .label { font-weight: 600; margin-right: 0.5rem; }
- .progress-bar { flex: 2; height: 8px; background: var(--bg-tertiary); border-radius: 4px; overflow: hidden; }
+ .progress-info .label { font-weight: var(--font-weight-semibold); margin-right: 0.5rem; }
+ .progress-bar { flex: 2; height: 8px; background: var(--color-surface-tertiary); border-radius: var(--radius-sm); overflow: hidden; }
.progress-bar .fill { height: 100%; background: var(--color-primary); transition: width 0.3s; }
/* Stats */
@@ -478,9 +478,9 @@ import {
}
.stat-card {
- background: var(--bg-primary);
- border: 1px solid var(--border-color);
- border-radius: 8px;
+ background: var(--color-surface-primary);
+ border: 1px solid var(--color-border-primary);
+ border-radius: var(--radius-lg);
padding: 1rem;
text-align: center;
}
@@ -491,8 +491,8 @@ import {
.stat-card.replayed { border-left: 4px solid var(--color-primary); }
.stat-card.failed { border-left: 4px solid var(--color-error); }
- .stat-value { display: block; font-size: 1.5rem; font-weight: 600; }
- .stat-label { font-size: 0.75rem; color: var(--text-secondary); }
+ .stat-value { display: block; font-size: 1.5rem; font-weight: var(--font-weight-semibold); }
+ .stat-label { font-size: 0.75rem; color: var(--color-text-secondary); }
/* Alert */
.alert {
@@ -500,7 +500,7 @@ import {
align-items: center;
gap: 0.75rem;
padding: 0.75rem 1rem;
- border-radius: 8px;
+ border-radius: var(--radius-lg);
margin-bottom: 1.5rem;
}
@@ -521,9 +521,9 @@ import {
}
.card {
- background: var(--bg-primary);
- border: 1px solid var(--border-color);
- border-radius: 8px;
+ background: var(--color-surface-primary);
+ border: 1px solid var(--color-border-primary);
+ border-radius: var(--radius-lg);
overflow: hidden;
}
@@ -532,10 +532,10 @@ import {
justify-content: space-between;
align-items: center;
padding: 1rem;
- border-bottom: 1px solid var(--border-color);
+ border-bottom: 1px solid var(--color-border-primary);
}
- .card-header h2 { margin: 0; font-size: 1rem; font-weight: 600; }
+ .card-header h2 { margin: 0; font-size: 1rem; font-weight: var(--font-weight-semibold); }
.link { color: var(--color-primary); text-decoration: none; font-size: 0.875rem; }
.link:hover { text-decoration: underline; }
.card-body { padding: 1rem; }
@@ -549,10 +549,10 @@ import {
gap: 0.5rem;
cursor: pointer;
}
- .bar-item:hover { background: var(--bg-secondary); }
+ .bar-item:hover { background: var(--color-surface-secondary); }
.bar-label { font-size: 0.875rem; }
- .bar-container { height: 16px; background: var(--bg-tertiary); border-radius: 4px; overflow: hidden; }
- .bar { height: 100%; background: var(--color-error); border-radius: 4px; }
+ .bar-container { height: 16px; background: var(--color-surface-tertiary); border-radius: var(--radius-sm); overflow: hidden; }
+ .bar { height: 100%; background: var(--color-error); border-radius: var(--radius-sm); }
.bar-value { font-size: 0.875rem; text-align: right; }
/* Tenant List */
@@ -562,10 +562,10 @@ import {
justify-content: space-between;
padding: 0.5rem;
cursor: pointer;
- border-radius: 4px;
+ border-radius: var(--radius-sm);
}
- .tenant-item:hover { background: var(--bg-secondary); }
- .tenant-count { font-weight: 600; }
+ .tenant-item:hover { background: var(--color-surface-secondary); }
+ .tenant-count { font-weight: var(--font-weight-semibold); }
/* Filters */
.filters-bar {
@@ -573,8 +573,8 @@ import {
flex-wrap: wrap;
gap: 1rem;
padding: 1rem;
- background: var(--bg-secondary);
- border-bottom: 1px solid var(--border-color);
+ background: var(--color-surface-secondary);
+ border-bottom: 1px solid var(--color-border-primary);
}
.filter-group {
@@ -583,12 +583,12 @@ import {
gap: 0.5rem;
}
- .filter-group label { font-size: 0.875rem; color: var(--text-secondary); }
+ .filter-group label { font-size: 0.875rem; color: var(--color-text-secondary); }
.filter-group select, .filter-group input {
padding: 0.5rem;
- border: 1px solid var(--border-color);
- border-radius: 4px;
- background: var(--bg-primary);
+ border: 1px solid var(--color-border-primary);
+ border-radius: var(--radius-sm);
+ background: var(--color-surface-primary);
}
/* Table */
@@ -596,10 +596,10 @@ import {
.data-table th, .data-table td {
padding: 0.75rem;
text-align: left;
- border-bottom: 1px solid var(--border-color);
+ border-bottom: 1px solid var(--color-border-primary);
}
- .data-table th { font-weight: 600; font-size: 0.875rem; background: var(--bg-secondary); }
- .data-table tbody tr:hover { background: var(--bg-secondary); }
+ .data-table th { font-weight: var(--font-weight-semibold); font-size: 0.875rem; background: var(--color-surface-secondary); }
+ .data-table tbody tr:hover { background: var(--color-surface-secondary); }
.checkbox-col { width: 40px; }
.monospace { font-family: monospace; font-size: 0.875rem; }
.entry-link { color: var(--color-primary); text-decoration: none; }
@@ -607,14 +607,14 @@ import {
.error-badge {
padding: 0.25rem 0.5rem;
- border-radius: 4px;
+ border-radius: var(--radius-sm);
font-size: 0.75rem;
- background: var(--bg-tertiary);
+ background: var(--color-surface-tertiary);
}
.status-badge {
padding: 0.25rem 0.5rem;
- border-radius: 4px;
+ border-radius: var(--radius-sm);
font-size: 0.75rem;
text-transform: capitalize;
}
@@ -633,16 +633,16 @@ import {
align-items: center;
gap: 1rem;
padding: 1rem;
- background: var(--bg-secondary);
- border-top: 1px solid var(--border-color);
+ background: var(--color-surface-secondary);
+ border-top: 1px solid var(--color-border-primary);
}
- .selection-count { font-weight: 600; }
+ .selection-count { font-weight: var(--font-weight-semibold); }
.empty-state, .loading-state {
padding: 2rem;
text-align: center;
- color: var(--text-secondary);
+ color: var(--color-text-secondary);
}
/* Modal */
@@ -660,8 +660,8 @@ import {
}
.modal {
- background: var(--bg-primary);
- border-radius: 8px;
+ background: var(--color-surface-primary);
+ border-radius: var(--radius-lg);
width: 100%;
max-width: 500px;
max-height: 90vh;
@@ -673,7 +673,7 @@ import {
justify-content: space-between;
align-items: center;
padding: 1rem;
- border-bottom: 1px solid var(--border-color);
+ border-bottom: 1px solid var(--color-border-primary);
}
.modal-header h3 { margin: 0; }
@@ -685,7 +685,7 @@ import {
justify-content: flex-end;
gap: 0.5rem;
padding: 1rem;
- border-top: 1px solid var(--border-color);
+ border-top: 1px solid var(--color-border-primary);
}
.replay-options, .resolve-options {
@@ -697,7 +697,7 @@ import {
.checkbox-label { display: flex; align-items: center; gap: 0.5rem; cursor: pointer; }
.option-group { display: flex; align-items: center; gap: 0.5rem; }
- .option-group select { padding: 0.5rem; border: 1px solid var(--border-color); border-radius: 4px; }
+ .option-group select { padding: 0.5rem; border: 1px solid var(--color-border-primary); border-radius: var(--radius-sm); }
.radio-group { display: flex; flex-direction: column; gap: 0.5rem; margin: 0.5rem 0; }
.radio-label { display: flex; align-items: center; gap: 0.5rem; cursor: pointer; }
@@ -705,12 +705,12 @@ import {
textarea {
width: 100%;
padding: 0.5rem;
- border: 1px solid var(--border-color);
- border-radius: 4px;
+ border: 1px solid var(--color-border-primary);
+ border-radius: var(--radius-sm);
resize: vertical;
}
- .note { font-size: 0.875rem; color: var(--text-secondary); font-style: italic; margin-top: 1rem; }
+ .note { font-size: 0.875rem; color: var(--color-text-secondary); font-style: italic; margin-top: 1rem; }
@media (max-width: 1024px) {
.dashboard-grid { grid-template-columns: 1fr; }
diff --git a/src/Web/StellaOps.Web/src/app/features/deadletter/deadletter-entry-detail.component.ts b/src/Web/StellaOps.Web/src/app/features/deadletter/deadletter-entry-detail.component.ts
index 0de615065..b4ee2779d 100644
--- a/src/Web/StellaOps.Web/src/app/features/deadletter/deadletter-entry-detail.component.ts
+++ b/src/Web/StellaOps.Web/src/app/features/deadletter/deadletter-entry-detail.component.ts
@@ -367,7 +367,7 @@ import {
}
.page-header h1 { margin: 0.5rem 0 0; font-size: 1.5rem; }
- .entry-id { margin: 0.25rem 0 0; font-family: monospace; color: var(--text-secondary); }
+ .entry-id { margin: 0.25rem 0 0; font-family: monospace; color: var(--color-text-secondary); }
.header-actions { display: flex; gap: 0.5rem; }
.btn {
@@ -375,18 +375,18 @@ import {
align-items: center;
gap: 0.5rem;
padding: 0.5rem 1rem;
- border: 1px solid var(--border-color);
- border-radius: 6px;
- background: var(--bg-primary);
+ border: 1px solid var(--color-border-primary);
+ border-radius: var(--radius-md);
+ background: var(--color-surface-primary);
cursor: pointer;
font-size: 0.875rem;
text-decoration: none;
}
- .btn:hover:not(:disabled) { background: var(--bg-tertiary); }
+ .btn:hover:not(:disabled) { background: var(--color-surface-tertiary); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
.btn-primary { background: var(--color-primary); color: var(--color-text-heading); border-color: var(--color-primary); }
- .btn-secondary { background: var(--bg-secondary); }
+ .btn-secondary { background: var(--color-surface-secondary); }
.btn-sm { padding: 0.25rem 0.5rem; font-size: 0.75rem; }
.btn-close { background: none; border: none; font-size: 1.5rem; cursor: pointer; }
@@ -396,7 +396,7 @@ import {
align-items: center;
gap: 1rem;
padding: 1rem;
- border-radius: 8px;
+ border-radius: var(--radius-lg);
margin-bottom: 1.5rem;
}
@@ -406,8 +406,8 @@ import {
.status-banner.status-replayed { background: var(--color-primary-bg); border: 1px solid var(--color-primary); }
.status-banner.status-failed { background: var(--color-error-bg); border: 1px solid var(--color-error); }
- .status-label { font-weight: 600; font-size: 1rem; }
- .status-detail { font-size: 0.875rem; color: var(--text-secondary); }
+ .status-label { font-weight: var(--font-weight-semibold); font-size: 1rem; }
+ .status-detail { font-size: 0.875rem; color: var(--color-text-secondary); }
/* Detail Grid */
.detail-grid {
@@ -417,9 +417,9 @@ import {
}
.card {
- background: var(--bg-primary);
- border: 1px solid var(--border-color);
- border-radius: 8px;
+ background: var(--color-surface-primary);
+ border: 1px solid var(--color-border-primary);
+ border-radius: var(--radius-lg);
overflow: hidden;
}
@@ -428,34 +428,34 @@ import {
justify-content: space-between;
align-items: center;
padding: 1rem;
- border-bottom: 1px solid var(--border-color);
+ border-bottom: 1px solid var(--color-border-primary);
}
- .card-header h2 { margin: 0; font-size: 1rem; font-weight: 600; }
+ .card-header h2 { margin: 0; font-size: 1rem; font-weight: var(--font-weight-semibold); }
.card-body { padding: 1rem; }
.detail-row {
display: flex;
gap: 1rem;
padding: 0.5rem 0;
- border-bottom: 1px solid var(--border-subtle);
+ border-bottom: 1px solid var(--color-border-primary);
}
.detail-row:last-child { border-bottom: none; }
- .detail-row .label { font-size: 0.875rem; color: var(--text-secondary); min-width: 100px; }
+ .detail-row .label { font-size: 0.875rem; color: var(--color-text-secondary); min-width: 100px; }
.detail-row .value { font-size: 0.875rem; flex: 1; }
.monospace { font-family: monospace; }
- .error-badge { font-weight: 600; color: var(--color-error); }
+ .error-badge { font-weight: var(--font-weight-semibold); color: var(--color-error); }
.error-message { word-break: break-word; }
- .age-warning { color: var(--color-error); font-weight: 600; }
+ .age-warning { color: var(--color-error); font-weight: var(--font-weight-semibold); }
.stack-trace { margin-top: 1rem; }
.stack-trace pre {
margin-top: 0.5rem;
padding: 1rem;
- background: var(--bg-secondary);
- border-radius: 4px;
+ background: var(--color-surface-secondary);
+ border-radius: var(--radius-sm);
overflow-x: auto;
font-size: 0.75rem;
}
@@ -463,8 +463,8 @@ import {
.payload-section { grid-column: span 2; }
.payload-json {
padding: 1rem;
- background: var(--bg-secondary);
- border-radius: 4px;
+ background: var(--color-surface-secondary);
+ border-radius: var(--radius-sm);
overflow-x: auto;
font-size: 0.875rem;
max-height: 300px;
@@ -490,8 +490,8 @@ import {
.timeline-marker {
width: 12px;
height: 12px;
- border-radius: 50%;
- background: var(--border-color);
+ border-radius: var(--radius-full);
+ background: var(--color-border-primary);
flex-shrink: 0;
margin-top: 4px;
}
@@ -502,9 +502,9 @@ import {
.timeline-item.action-failed .timeline-marker { background: var(--color-error); }
.timeline-content { display: flex; flex-wrap: wrap; gap: 0.5rem; font-size: 0.875rem; }
- .timeline-time { color: var(--text-secondary); }
- .timeline-action { font-weight: 500; }
- .timeline-actor { color: var(--text-secondary); }
+ .timeline-time { color: var(--color-text-secondary); }
+ .timeline-action { font-weight: var(--font-weight-medium); }
+ .timeline-actor { color: var(--color-text-secondary); }
/* Modal */
.modal-overlay {
@@ -521,8 +521,8 @@ import {
}
.modal {
- background: var(--bg-primary);
- border-radius: 8px;
+ background: var(--color-surface-primary);
+ border-radius: var(--radius-lg);
width: 100%;
max-width: 500px;
}
@@ -532,7 +532,7 @@ import {
justify-content: space-between;
align-items: center;
padding: 1rem;
- border-bottom: 1px solid var(--border-color);
+ border-bottom: 1px solid var(--color-border-primary);
}
.modal-header h3 { margin: 0; }
@@ -542,18 +542,18 @@ import {
justify-content: flex-end;
gap: 0.5rem;
padding: 1rem;
- border-top: 1px solid var(--border-color);
+ border-top: 1px solid var(--color-border-primary);
}
.replay-options, .resolve-options { display: flex; flex-direction: column; gap: 0.75rem; margin-top: 1rem; }
.checkbox-label { display: flex; align-items: center; gap: 0.5rem; cursor: pointer; }
.option-group { display: flex; align-items: center; gap: 0.5rem; }
- .option-group select { padding: 0.5rem; border: 1px solid var(--border-color); border-radius: 4px; }
+ .option-group select { padding: 0.5rem; border: 1px solid var(--color-border-primary); border-radius: var(--radius-sm); }
.radio-group { display: flex; flex-direction: column; gap: 0.5rem; margin: 0.5rem 0; }
.radio-label { display: flex; align-items: center; gap: 0.5rem; cursor: pointer; }
- textarea { width: 100%; padding: 0.5rem; border: 1px solid var(--border-color); border-radius: 4px; }
+ textarea { width: 100%; padding: 0.5rem; border: 1px solid var(--color-border-primary); border-radius: var(--radius-sm); }
- .loading-state, .empty-state { padding: 3rem; text-align: center; color: var(--text-secondary); }
+ .loading-state, .empty-state { padding: 3rem; text-align: center; color: var(--color-text-secondary); }
@media (max-width: 1024px) {
.detail-grid { grid-template-columns: 1fr; }
diff --git a/src/Web/StellaOps.Web/src/app/features/deadletter/deadletter-queue.component.ts b/src/Web/StellaOps.Web/src/app/features/deadletter/deadletter-queue.component.ts
index 69dce7d13..ce9c2f611 100644
--- a/src/Web/StellaOps.Web/src/app/features/deadletter/deadletter-queue.component.ts
+++ b/src/Web/StellaOps.Web/src/app/features/deadletter/deadletter-queue.component.ts
@@ -261,7 +261,7 @@ import {
.subtitle {
margin: 0.25rem 0 0;
- color: var(--text-secondary);
+ color: var(--color-text-secondary);
}
.header-actions {
@@ -274,17 +274,17 @@ import {
align-items: center;
gap: 0.5rem;
padding: 0.5rem 1rem;
- border: 1px solid var(--border-color);
- border-radius: 6px;
- background: var(--bg-primary);
+ border: 1px solid var(--color-border-primary);
+ border-radius: var(--radius-md);
+ background: var(--color-surface-primary);
cursor: pointer;
font-size: 0.875rem;
text-decoration: none;
}
- .btn:hover:not(:disabled) { background: var(--bg-tertiary); }
+ .btn:hover:not(:disabled) { background: var(--color-surface-tertiary); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
- .btn-secondary { background: var(--bg-secondary); }
+ .btn-secondary { background: var(--color-surface-secondary); }
.btn-sm { padding: 0.25rem 0.5rem; font-size: 0.75rem; }
.btn-icon { padding: 0.5rem; }
.spinning { animation: spin 1s linear infinite; }
@@ -292,9 +292,9 @@ import {
/* Filters */
.filters-section {
- background: var(--bg-primary);
- border: 1px solid var(--border-color);
- border-radius: 8px;
+ background: var(--color-surface-primary);
+ border: 1px solid var(--color-border-primary);
+ border-radius: var(--radius-lg);
padding: 1rem;
margin-bottom: 1.5rem;
}
@@ -314,15 +314,15 @@ import {
.filter-group label {
font-size: 0.75rem;
- color: var(--text-secondary);
+ color: var(--color-text-secondary);
}
.filter-group select,
.filter-group input {
padding: 0.5rem;
- border: 1px solid var(--border-color);
- border-radius: 4px;
- background: var(--bg-primary);
+ border: 1px solid var(--color-border-primary);
+ border-radius: var(--radius-sm);
+ background: var(--color-surface-primary);
}
.filter-actions {
@@ -333,14 +333,14 @@ import {
.result-count {
font-size: 0.875rem;
- color: var(--text-secondary);
+ color: var(--color-text-secondary);
}
/* Table */
.table-section {
- background: var(--bg-primary);
- border: 1px solid var(--border-color);
- border-radius: 8px;
+ background: var(--color-surface-primary);
+ border: 1px solid var(--color-border-primary);
+ border-radius: var(--radius-lg);
overflow: hidden;
}
@@ -357,13 +357,13 @@ import {
.data-table td {
padding: 0.75rem;
text-align: left;
- border-bottom: 1px solid var(--border-color);
+ border-bottom: 1px solid var(--color-border-primary);
}
.data-table th {
- font-weight: 600;
+ font-weight: var(--font-weight-semibold);
font-size: 0.875rem;
- background: var(--bg-secondary);
+ background: var(--color-surface-secondary);
}
.data-table th.sortable {
@@ -371,7 +371,7 @@ import {
}
.data-table th.sortable:hover {
- background: var(--bg-tertiary);
+ background: var(--color-surface-tertiary);
}
.sort-icon {
@@ -379,7 +379,7 @@ import {
}
.data-table tbody tr:hover {
- background: var(--bg-secondary);
+ background: var(--color-surface-secondary);
}
.checkbox-col { width: 40px; }
@@ -402,15 +402,15 @@ import {
.error-badge {
display: inline-block;
padding: 0.25rem 0.5rem;
- border-radius: 4px;
+ border-radius: var(--radius-sm);
font-size: 0.75rem;
- background: var(--bg-tertiary);
+ background: var(--color-surface-tertiary);
}
.status-badge {
display: inline-block;
padding: 0.25rem 0.5rem;
- border-radius: 4px;
+ border-radius: var(--radius-sm);
font-size: 0.75rem;
text-transform: capitalize;
}
@@ -423,14 +423,14 @@ import {
.age-warning {
color: var(--color-error);
- font-weight: 600;
+ font-weight: var(--font-weight-semibold);
}
.empty-state,
.loading-state {
padding: 3rem;
text-align: center;
- color: var(--text-secondary);
+ color: var(--color-text-secondary);
}
.pagination {
@@ -439,12 +439,12 @@ import {
align-items: center;
gap: 1rem;
padding: 1rem;
- border-top: 1px solid var(--border-color);
+ border-top: 1px solid var(--color-border-primary);
}
.page-info {
font-size: 0.875rem;
- color: var(--text-secondary);
+ color: var(--color-text-secondary);
}
/* Bulk Actions */
@@ -457,14 +457,14 @@ import {
align-items: center;
gap: 1rem;
padding: 1rem 2rem;
- background: var(--bg-primary);
+ background: var(--color-surface-primary);
border-top: 2px solid var(--color-primary);
box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.1);
z-index: 100;
}
.selection-count {
- font-weight: 600;
+ font-weight: var(--font-weight-semibold);
}
`]
})
diff --git a/src/Web/StellaOps.Web/src/app/features/deploy-diff/components/component-diff-row/component-diff-row.component.ts b/src/Web/StellaOps.Web/src/app/features/deploy-diff/components/component-diff-row/component-diff-row.component.ts
index b38fd9393..22426ba8f 100644
--- a/src/Web/StellaOps.Web/src/app/features/deploy-diff/components/component-diff-row/component-diff-row.component.ts
+++ b/src/Web/StellaOps.Web/src/app/features/deploy-diff/components/component-diff-row/component-diff-row.component.ts
@@ -215,15 +215,15 @@ import {
`,
styles: [`
.diff-row {
- border-bottom: 1px solid var(--border-light);
+ border-bottom: 1px solid var(--color-border-primary);
transition: background-color 0.15s;
&:hover {
- background-color: var(--surface-hover);
+ background-color: var(--color-nav-hover);
}
&.expanded {
- background-color: var(--surface-secondary);
+ background-color: var(--color-surface-secondary);
}
&.change-added {
@@ -260,10 +260,10 @@ import {
gap: 0.375rem;
padding: 0.25rem 0.5rem;
font-size: 0.6875rem;
- font-weight: 600;
+ font-weight: var(--font-weight-semibold);
text-transform: uppercase;
letter-spacing: 0.025em;
- border-radius: 4px;
+ border-radius: var(--radius-sm);
&.badge-added {
background-color: var(--color-success-bg);
@@ -296,12 +296,12 @@ import {
}
.package-group {
- color: var(--text-muted);
+ color: var(--color-text-muted);
}
.package-name {
- color: var(--text-primary);
- font-weight: 500;
+ color: var(--color-text-primary);
+ font-weight: var(--font-weight-medium);
}
/* Version */
@@ -314,7 +314,7 @@ import {
}
.version-from {
- color: var(--text-secondary);
+ color: var(--color-text-secondary);
&.version-removed {
color: var(--color-error);
@@ -323,28 +323,28 @@ import {
}
.version-arrow {
- color: var(--text-muted);
+ color: var(--color-text-muted);
}
.version-to {
- color: var(--text-primary);
+ color: var(--color-text-primary);
&.version-added {
color: var(--color-success);
- font-weight: 500;
+ font-weight: var(--font-weight-medium);
}
}
.version-unchanged {
- color: var(--text-muted);
+ color: var(--color-text-muted);
}
.version-badge {
padding: 0.125rem 0.375rem;
font-size: 0.625rem;
- font-weight: 600;
+ font-weight: var(--font-weight-semibold);
text-transform: uppercase;
- border-radius: 3px;
+ border-radius: var(--radius-sm);
&--major {
background-color: var(--color-error-bg);
@@ -365,7 +365,7 @@ import {
/* License */
.diff-row__license {
font-size: 0.75rem;
- color: var(--text-secondary);
+ color: var(--color-text-secondary);
}
.license-changed {
@@ -389,14 +389,14 @@ import {
padding: 0.25rem;
background: transparent;
border: none;
- color: var(--text-muted);
+ color: var(--color-text-muted);
cursor: pointer;
- border-radius: 4px;
+ border-radius: var(--radius-sm);
transition: all 0.15s;
&:hover {
- background-color: var(--surface-hover);
- color: var(--text-primary);
+ background-color: var(--color-nav-hover);
+ color: var(--color-text-primary);
}
svg {
@@ -411,8 +411,8 @@ import {
/* Details section */
.diff-row__details {
padding: 1rem 1rem 1rem 2rem;
- border-top: 1px solid var(--border-light);
- background-color: var(--surface-primary);
+ border-top: 1px solid var(--color-border-primary);
+ background-color: var(--color-surface-primary);
}
.details-section {
@@ -426,8 +426,8 @@ import {
.details-section__title {
margin: 0 0 0.5rem;
font-size: 0.75rem;
- font-weight: 600;
- color: var(--text-secondary);
+ font-weight: var(--font-weight-semibold);
+ color: var(--color-text-secondary);
text-transform: uppercase;
letter-spacing: 0.025em;
}
@@ -449,8 +449,8 @@ import {
padding: 0.25rem 0.5rem;
font-size: 0.75rem;
font-family: 'SF Mono', 'Consolas', monospace;
- background-color: var(--surface-secondary);
- border-radius: 4px;
+ background-color: var(--color-surface-secondary);
+ border-radius: var(--radius-sm);
&.direct {
background-color: var(--color-info-bg);
@@ -458,21 +458,21 @@ import {
}
.dep-name {
- color: var(--text-primary);
+ color: var(--color-text-primary);
}
.dep-version {
- color: var(--text-muted);
+ color: var(--color-text-muted);
}
.dep-badge {
font-size: 0.625rem;
color: var(--color-info);
- font-weight: 500;
+ font-weight: var(--font-weight-medium);
}
.dependency-more {
- color: var(--text-muted);
+ color: var(--color-text-muted);
font-size: 0.75rem;
font-style: italic;
}
@@ -490,7 +490,7 @@ import {
gap: 0.75rem;
padding: 0.375rem 0;
font-size: 0.75rem;
- border-bottom: 1px solid var(--border-light);
+ border-bottom: 1px solid var(--color-border-primary);
&:last-child {
border-bottom: none;
@@ -515,7 +515,7 @@ import {
.vuln-link {
font-family: 'SF Mono', 'Consolas', monospace;
- color: var(--text-link);
+ color: var(--color-brand-primary);
text-decoration: none;
&:hover {
@@ -524,21 +524,21 @@ import {
}
.vuln-severity {
- font-weight: 600;
+ font-weight: var(--font-weight-semibold);
text-transform: capitalize;
}
.vuln-score {
- color: var(--text-muted);
+ color: var(--color-text-muted);
}
.vuln-fixed {
padding: 0.125rem 0.375rem;
font-size: 0.625rem;
- font-weight: 600;
+ font-weight: var(--font-weight-semibold);
background-color: var(--color-success-bg);
color: var(--color-success);
- border-radius: 3px;
+ border-radius: var(--radius-sm);
}
/* PURL */
@@ -547,8 +547,8 @@ import {
padding: 0.5rem;
font-size: 0.75rem;
font-family: 'SF Mono', 'Consolas', monospace;
- background-color: var(--surface-secondary);
- border-radius: 4px;
+ background-color: var(--color-surface-secondary);
+ border-radius: var(--radius-sm);
word-break: break-all;
}
`],
diff --git a/src/Web/StellaOps.Web/src/app/features/deploy-diff/components/deploy-action-bar/deploy-action-bar.component.ts b/src/Web/StellaOps.Web/src/app/features/deploy-diff/components/deploy-action-bar/deploy-action-bar.component.ts
index b0df05b68..f56c35dcb 100644
--- a/src/Web/StellaOps.Web/src/app/features/deploy-diff/components/deploy-action-bar/deploy-action-bar.component.ts
+++ b/src/Web/StellaOps.Web/src/app/features/deploy-diff/components/deploy-action-bar/deploy-action-bar.component.ts
@@ -164,8 +164,8 @@ import {
justify-content: space-between;
gap: 1rem;
padding: 0.75rem 1rem;
- background: var(--surface-secondary);
- border-top: 1px solid var(--border-default);
+ background: var(--color-surface-secondary);
+ border-top: 1px solid var(--color-border-primary);
position: sticky;
bottom: 0;
}
@@ -187,8 +187,8 @@ import {
gap: 0.375rem;
padding: 0.25rem 0.625rem;
font-size: 0.75rem;
- font-weight: 500;
- border-radius: 4px;
+ font-weight: var(--font-weight-medium);
+ border-radius: var(--radius-sm);
&--pass {
background-color: var(--color-success-bg);
@@ -219,9 +219,9 @@ import {
gap: 0.5rem;
padding: 0.5rem 1rem;
font-size: 0.875rem;
- font-weight: 500;
+ font-weight: var(--font-weight-medium);
border: none;
- border-radius: 6px;
+ border-radius: var(--radius-md);
cursor: pointer;
transition: all 0.15s;
@@ -268,7 +268,7 @@ import {
height: 16px;
border: 2px solid rgba(255, 255, 255, 0.3);
border-top-color: white;
- border-radius: 50%;
+ border-radius: var(--radius-full);
animation: spin 0.8s linear infinite;
}
@@ -290,7 +290,7 @@ import {
align-items: center;
gap: 0.25rem;
font-size: 0.6875rem;
- color: var(--text-muted);
+ color: var(--color-text-muted);
kbd {
display: inline-flex;
@@ -301,10 +301,10 @@ import {
padding: 0 0.25rem;
font-family: 'SF Mono', 'Consolas', monospace;
font-size: 0.625rem;
- background: var(--surface-primary);
- border: 1px solid var(--border-default);
- border-radius: 3px;
- box-shadow: 0 1px 0 var(--border-default);
+ background: var(--color-surface-primary);
+ border: 1px solid var(--color-border-primary);
+ border-radius: var(--radius-sm);
+ box-shadow: 0 1px 0 var(--color-border-primary);
}
}
diff --git a/src/Web/StellaOps.Web/src/app/features/deploy-diff/components/deploy-diff-panel/deploy-diff-panel.component.ts b/src/Web/StellaOps.Web/src/app/features/deploy-diff/components/deploy-diff-panel/deploy-diff-panel.component.ts
index fbdec69f1..5d36898e7 100644
--- a/src/Web/StellaOps.Web/src/app/features/deploy-diff/components/deploy-diff-panel/deploy-diff-panel.component.ts
+++ b/src/Web/StellaOps.Web/src/app/features/deploy-diff/components/deploy-diff-panel/deploy-diff-panel.component.ts
@@ -208,9 +208,9 @@ import { OverrideDialogComponent } from '../override-dialog/override-dialog.comp
display: flex;
flex-direction: column;
height: 100%;
- background: var(--surface-primary);
- border: 1px solid var(--border-default);
- border-radius: 8px;
+ background: var(--color-surface-primary);
+ border: 1px solid var(--color-border-primary);
+ border-radius: var(--radius-lg);
overflow: hidden;
}
@@ -220,8 +220,8 @@ import { OverrideDialogComponent } from '../override-dialog/override-dialog.comp
align-items: center;
justify-content: space-between;
padding: 1rem 1.25rem;
- background: var(--surface-primary);
- border-bottom: 1px solid var(--border-default);
+ background: var(--color-surface-primary);
+ border-bottom: 1px solid var(--color-border-primary);
}
.header-content {
@@ -236,8 +236,8 @@ import { OverrideDialogComponent } from '../override-dialog/override-dialog.comp
gap: 0.5rem;
margin: 0;
font-size: 1.125rem;
- font-weight: 600;
- color: var(--text-primary);
+ font-weight: var(--font-weight-semibold);
+ color: var(--color-text-primary);
svg {
color: var(--color-primary);
@@ -255,11 +255,11 @@ import { OverrideDialogComponent } from '../override-dialog/override-dialog.comp
padding: 0.25rem 0.5rem;
font-family: 'SF Mono', 'Consolas', monospace;
font-size: 0.75rem;
- border-radius: 4px;
+ border-radius: var(--radius-sm);
&--from {
- background: var(--surface-secondary);
- color: var(--text-secondary);
+ background: var(--color-surface-secondary);
+ color: var(--color-text-secondary);
}
&--to {
@@ -269,7 +269,7 @@ import { OverrideDialogComponent } from '../override-dialog/override-dialog.comp
}
.arrow {
- color: var(--text-muted);
+ color: var(--color-text-muted);
}
.header-actions {
@@ -281,15 +281,15 @@ import { OverrideDialogComponent } from '../override-dialog/override-dialog.comp
.refresh-btn {
padding: 0.5rem;
background: transparent;
- border: 1px solid var(--border-default);
- border-radius: 6px;
- color: var(--text-secondary);
+ border: 1px solid var(--color-border-primary);
+ border-radius: var(--radius-md);
+ color: var(--color-text-secondary);
cursor: pointer;
transition: all 0.15s;
&:hover:not(:disabled) {
- background: var(--surface-hover);
- color: var(--text-primary);
+ background: var(--color-nav-hover);
+ color: var(--color-text-primary);
}
&:disabled {
@@ -314,8 +314,8 @@ import { OverrideDialogComponent } from '../override-dialog/override-dialog.comp
align-items: center;
gap: 1.5rem;
padding: 0.625rem 1.25rem;
- background: var(--surface-secondary);
- border-bottom: 1px solid var(--border-default);
+ background: var(--color-surface-secondary);
+ border-bottom: 1px solid var(--color-border-primary);
}
.summary-item {
@@ -323,10 +323,10 @@ import { OverrideDialogComponent } from '../override-dialog/override-dialog.comp
align-items: center;
gap: 0.375rem;
font-size: 0.8125rem;
- color: var(--text-secondary);
+ color: var(--color-text-secondary);
strong {
- font-weight: 600;
+ font-weight: var(--font-weight-semibold);
}
&--added {
@@ -375,10 +375,10 @@ import { OverrideDialogComponent } from '../override-dialog/override-dialog.comp
.skeleton-bar {
height: 1rem;
- background: linear-gradient(90deg, var(--surface-secondary) 25%, var(--surface-tertiary) 50%, var(--surface-secondary) 75%);
+ background: linear-gradient(90deg, var(--color-surface-secondary) 25%, var(--color-surface-tertiary) 50%, var(--color-surface-secondary) 75%);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
- border-radius: 4px;
+ border-radius: var(--radius-sm);
&--wide { width: 200px; }
&--medium { width: 120px; }
@@ -409,14 +409,14 @@ import { OverrideDialogComponent } from '../override-dialog/override-dialog.comp
h3 {
margin: 0;
font-size: 1rem;
- font-weight: 600;
- color: var(--text-primary);
+ font-weight: var(--font-weight-semibold);
+ color: var(--color-text-primary);
}
p {
margin: 0;
font-size: 0.875rem;
- color: var(--text-secondary);
+ color: var(--color-text-secondary);
max-width: 400px;
}
}
@@ -427,11 +427,11 @@ import { OverrideDialogComponent } from '../override-dialog/override-dialog.comp
gap: 0.5rem;
padding: 0.5rem 1rem;
font-size: 0.875rem;
- font-weight: 500;
- color: var(--text-link);
+ font-weight: var(--font-weight-medium);
+ color: var(--color-brand-primary);
background: transparent;
border: 1px solid var(--color-primary);
- border-radius: 6px;
+ border-radius: var(--radius-md);
cursor: pointer;
transition: all 0.15s;
diff --git a/src/Web/StellaOps.Web/src/app/features/deploy-diff/components/override-dialog/override-dialog.component.ts b/src/Web/StellaOps.Web/src/app/features/deploy-diff/components/override-dialog/override-dialog.component.ts
index 80762791b..846a5e7a3 100644
--- a/src/Web/StellaOps.Web/src/app/features/deploy-diff/components/override-dialog/override-dialog.component.ts
+++ b/src/Web/StellaOps.Web/src/app/features/deploy-diff/components/override-dialog/override-dialog.component.ts
@@ -212,8 +212,8 @@ import {
max-width: 560px;
max-height: 90vh;
overflow-y: auto;
- background: var(--surface-primary);
- border-radius: 12px;
+ background: var(--color-surface-primary);
+ border-radius: var(--radius-xl);
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
@@ -223,7 +223,7 @@ import {
align-items: center;
justify-content: space-between;
padding: 1rem 1.25rem;
- border-bottom: 1px solid var(--border-default);
+ border-bottom: 1px solid var(--color-border-primary);
}
.dialog__title {
@@ -232,8 +232,8 @@ import {
gap: 0.5rem;
margin: 0;
font-size: 1.125rem;
- font-weight: 600;
- color: var(--text-primary);
+ font-weight: var(--font-weight-semibold);
+ color: var(--color-text-primary);
svg {
color: var(--color-warning);
@@ -244,14 +244,14 @@ import {
padding: 0.375rem;
background: transparent;
border: none;
- color: var(--text-muted);
- border-radius: 6px;
+ color: var(--color-text-muted);
+ border-radius: var(--radius-md);
cursor: pointer;
transition: all 0.15s;
&:hover {
- background: var(--surface-hover);
- color: var(--text-primary);
+ background: var(--color-nav-hover);
+ color: var(--color-text-primary);
}
}
@@ -293,8 +293,8 @@ import {
display: block;
margin-bottom: 0.5rem;
font-size: 0.875rem;
- font-weight: 500;
- color: var(--text-primary);
+ font-weight: var(--font-weight-medium);
+ color: var(--color-text-primary);
}
.required {
@@ -302,8 +302,8 @@ import {
}
.optional {
- font-weight: 400;
- color: var(--text-muted);
+ font-weight: var(--font-weight-normal);
+ color: var(--color-text-muted);
}
.form-textarea,
@@ -311,10 +311,10 @@ import {
width: 100%;
padding: 0.625rem 0.75rem;
font-size: 0.875rem;
- color: var(--text-primary);
- background: var(--surface-primary);
- border: 1px solid var(--border-default);
- border-radius: 6px;
+ color: var(--color-text-primary);
+ background: var(--color-surface-primary);
+ border: 1px solid var(--color-border-primary);
+ border-radius: var(--radius-md);
transition: border-color 0.15s, box-shadow 0.15s;
&:focus {
@@ -332,7 +332,7 @@ import {
}
&::placeholder {
- color: var(--text-muted);
+ color: var(--color-text-muted);
}
}
@@ -352,7 +352,7 @@ import {
display: block;
margin-top: 0.375rem;
font-size: 0.75rem;
- color: var(--text-muted);
+ color: var(--color-text-muted);
}
/* Policy list */
@@ -369,7 +369,7 @@ import {
padding: 0.5rem 0.75rem;
font-size: 0.8125rem;
background: var(--color-error-bg);
- border-radius: 4px;
+ border-radius: var(--radius-sm);
margin-bottom: 0.375rem;
&:last-child {
@@ -378,28 +378,28 @@ import {
}
.policy-gate {
- font-weight: 600;
+ font-weight: var(--font-weight-semibold);
color: var(--color-error);
}
.policy-message {
- color: var(--text-secondary);
+ color: var(--color-text-secondary);
}
/* Signer preview */
.signer-preview {
padding: 1rem;
- background: var(--surface-secondary);
- border-radius: 8px;
+ background: var(--color-surface-secondary);
+ border-radius: var(--radius-lg);
}
.signer-title {
margin: 0 0 0.75rem;
font-size: 0.75rem;
- font-weight: 500;
+ font-weight: var(--font-weight-medium);
text-transform: uppercase;
letter-spacing: 0.05em;
- color: var(--text-muted);
+ color: var(--color-text-muted);
}
.signer-info {
@@ -415,10 +415,10 @@ import {
width: 40px;
height: 40px;
font-size: 0.875rem;
- font-weight: 600;
+ font-weight: var(--font-weight-semibold);
color: white;
background: var(--color-primary);
- border-radius: 50%;
+ border-radius: var(--radius-full);
}
.signer-details {
@@ -428,19 +428,19 @@ import {
.signer-name {
display: block;
font-size: 0.875rem;
- font-weight: 500;
- color: var(--text-primary);
+ font-weight: var(--font-weight-medium);
+ color: var(--color-text-primary);
}
.signer-email {
display: block;
font-size: 0.75rem;
- color: var(--text-secondary);
+ color: var(--color-text-secondary);
}
.signer-timestamp {
font-size: 0.75rem;
- color: var(--text-muted);
+ color: var(--color-text-muted);
}
/* Footer */
@@ -449,8 +449,8 @@ import {
justify-content: flex-end;
gap: 0.75rem;
padding: 1rem 1.25rem;
- background: var(--surface-secondary);
- border-top: 1px solid var(--border-default);
+ background: var(--color-surface-secondary);
+ border-top: 1px solid var(--color-border-primary);
}
.btn {
@@ -459,9 +459,9 @@ import {
gap: 0.5rem;
padding: 0.5rem 1rem;
font-size: 0.875rem;
- font-weight: 500;
+ font-weight: var(--font-weight-medium);
border: none;
- border-radius: 6px;
+ border-radius: var(--radius-md);
cursor: pointer;
transition: all 0.15s;
@@ -471,12 +471,12 @@ import {
}
&--secondary {
- background: var(--surface-primary);
- color: var(--text-secondary);
- border: 1px solid var(--border-default);
+ background: var(--color-surface-primary);
+ color: var(--color-text-secondary);
+ border: 1px solid var(--color-border-primary);
&:hover:not(:disabled) {
- background: var(--surface-hover);
+ background: var(--color-nav-hover);
}
}
@@ -495,7 +495,7 @@ import {
height: 14px;
border: 2px solid rgba(255, 255, 255, 0.3);
border-top-color: white;
- border-radius: 50%;
+ border-radius: var(--radius-full);
animation: spin 0.8s linear infinite;
}
diff --git a/src/Web/StellaOps.Web/src/app/features/deploy-diff/components/policy-hit-annotation/policy-hit-annotation.component.ts b/src/Web/StellaOps.Web/src/app/features/deploy-diff/components/policy-hit-annotation/policy-hit-annotation.component.ts
index 316cc430f..a713cf0ba 100644
--- a/src/Web/StellaOps.Web/src/app/features/deploy-diff/components/policy-hit-annotation/policy-hit-annotation.component.ts
+++ b/src/Web/StellaOps.Web/src/app/features/deploy-diff/components/policy-hit-annotation/policy-hit-annotation.component.ts
@@ -169,8 +169,8 @@ import {
gap: 0.25rem;
padding: 0.125rem 0.5rem;
font-size: 0.6875rem;
- font-weight: 500;
- border-radius: 20px;
+ font-weight: var(--font-weight-medium);
+ border-radius: var(--radius-2xl);
white-space: nowrap;
&.valid {
@@ -212,8 +212,8 @@ import {
}
&.vex-unknown {
- background-color: var(--surface-secondary);
- color: var(--text-secondary);
+ background-color: var(--color-surface-secondary);
+ color: var(--color-text-secondary);
}
}
@@ -230,9 +230,9 @@ import {
gap: 0.375rem;
padding: 0.125rem 0.5rem;
font-size: 0.6875rem;
- font-weight: 500;
+ font-weight: var(--font-weight-medium);
text-decoration: none;
- border-radius: 4px;
+ border-radius: var(--radius-sm);
transition: opacity 0.15s;
&:hover {
@@ -256,7 +256,7 @@ import {
}
.policy-result {
- font-weight: 600;
+ font-weight: var(--font-weight-semibold);
}
.policy-gate {
@@ -266,7 +266,7 @@ import {
.more-hits-btn {
padding: 0.125rem 0.5rem;
font-size: 0.6875rem;
- color: var(--text-link);
+ color: var(--color-brand-primary);
background: transparent;
border: none;
cursor: pointer;
@@ -283,8 +283,8 @@ import {
gap: 0.375rem;
padding: 0.25rem 0.5rem;
font-size: 0.75rem;
- font-weight: 500;
- border-radius: 4px;
+ font-weight: var(--font-weight-medium);
+ border-radius: var(--radius-sm);
&.status-pass {
background-color: var(--color-success-bg);
diff --git a/src/Web/StellaOps.Web/src/app/features/deploy-diff/components/sbom-side-by-side/sbom-side-by-side.component.ts b/src/Web/StellaOps.Web/src/app/features/deploy-diff/components/sbom-side-by-side/sbom-side-by-side.component.ts
index fc1fc58f5..f2a1d5ac5 100644
--- a/src/Web/StellaOps.Web/src/app/features/deploy-diff/components/sbom-side-by-side/sbom-side-by-side.component.ts
+++ b/src/Web/StellaOps.Web/src/app/features/deploy-diff/components/sbom-side-by-side/sbom-side-by-side.component.ts
@@ -263,9 +263,9 @@ interface UnifiedRow {
display: flex;
flex-direction: column;
height: 100%;
- background: var(--surface-primary);
- border: 1px solid var(--border-default);
- border-radius: 8px;
+ background: var(--color-surface-primary);
+ border: 1px solid var(--color-border-primary);
+ border-radius: var(--radius-lg);
overflow: hidden;
}
@@ -273,7 +273,7 @@ interface UnifiedRow {
.sbom-headers {
display: grid;
grid-template-columns: 1fr 1fr;
- border-bottom: 1px solid var(--border-default);
+ border-bottom: 1px solid var(--color-border-primary);
}
.sbom-header {
@@ -281,10 +281,10 @@ interface UnifiedRow {
flex-direction: column;
gap: 0.25rem;
padding: 0.75rem 1rem;
- background: var(--surface-secondary);
+ background: var(--color-surface-secondary);
&--left {
- border-right: 1px solid var(--border-default);
+ border-right: 1px solid var(--color-border-primary);
}
}
@@ -294,23 +294,23 @@ interface UnifiedRow {
gap: 0.5rem;
margin: 0;
font-size: 0.875rem;
- font-weight: 600;
- color: var(--text-primary);
+ font-weight: var(--font-weight-semibold);
+ color: var(--color-text-primary);
svg {
- color: var(--text-secondary);
+ color: var(--color-text-secondary);
}
}
.header-label {
font-size: 0.75rem;
font-family: 'SF Mono', 'Consolas', monospace;
- color: var(--text-secondary);
+ color: var(--color-text-secondary);
}
.header-count {
font-size: 0.6875rem;
- color: var(--text-muted);
+ color: var(--color-text-muted);
}
/* Content */
@@ -331,10 +331,10 @@ interface UnifiedRow {
.unified-row {
display: grid;
grid-template-columns: 1fr 40px 1fr;
- border-bottom: 1px solid var(--border-light);
+ border-bottom: 1px solid var(--color-border-primary);
&:hover {
- background-color: var(--surface-hover);
+ background-color: var(--color-nav-hover);
}
&.row-added {
@@ -350,7 +350,7 @@ interface UnifiedRow {
}
&.expanded {
- background-color: var(--surface-secondary);
+ background-color: var(--color-surface-secondary);
}
}
@@ -361,11 +361,11 @@ interface UnifiedRow {
align-items: center;
&--left {
- border-right: 1px solid var(--border-light);
+ border-right: 1px solid var(--color-border-primary);
}
&--right {
- border-left: 1px solid var(--border-light);
+ border-left: 1px solid var(--color-border-primary);
}
}
@@ -403,8 +403,8 @@ interface UnifiedRow {
.component-name {
font-family: 'SF Mono', 'Consolas', monospace;
font-size: 0.8125rem;
- font-weight: 500;
- color: var(--text-primary);
+ font-weight: var(--font-weight-medium);
+ color: var(--color-text-primary);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
@@ -413,12 +413,12 @@ interface UnifiedRow {
.component-version {
font-family: 'SF Mono', 'Consolas', monospace;
font-size: 0.75rem;
- color: var(--text-secondary);
+ color: var(--color-text-secondary);
}
.component-license {
font-size: 0.6875rem;
- color: var(--text-muted);
+ color: var(--color-text-muted);
}
/* Placeholder cell */
@@ -433,7 +433,7 @@ interface UnifiedRow {
.placeholder-line {
width: 60%;
height: 2px;
- background: var(--border-light);
+ background: var(--color-border-primary);
border-radius: 1px;
}
@@ -442,7 +442,7 @@ interface UnifiedRow {
display: flex;
align-items: center;
justify-content: center;
- background: var(--surface-secondary);
+ background: var(--color-surface-secondary);
}
.diff-badge {
@@ -452,8 +452,8 @@ interface UnifiedRow {
width: 20px;
height: 20px;
font-size: 0.75rem;
- font-weight: 600;
- border-radius: 4px;
+ font-weight: var(--font-weight-semibold);
+ border-radius: var(--radius-sm);
&--added {
background-color: var(--color-success);
@@ -475,8 +475,8 @@ interface UnifiedRow {
.expanded-details {
grid-column: 1 / -1;
padding: 0;
- border-top: 1px solid var(--border-light);
- background: var(--surface-primary);
+ border-top: 1px solid var(--color-border-primary);
+ background: var(--color-surface-primary);
}
/* Empty state */
@@ -487,7 +487,7 @@ interface UnifiedRow {
justify-content: center;
gap: 0.75rem;
padding: 3rem 1rem;
- color: var(--text-muted);
+ color: var(--color-text-muted);
svg {
opacity: 0.5;
@@ -496,8 +496,8 @@ interface UnifiedRow {
p {
margin: 0;
font-size: 0.875rem;
- font-weight: 500;
- color: var(--text-secondary);
+ font-weight: var(--font-weight-medium);
+ color: var(--color-text-secondary);
}
}
diff --git a/src/Web/StellaOps.Web/src/app/features/deploy-diff/pages/deploy-diff.page.ts b/src/Web/StellaOps.Web/src/app/features/deploy-diff/pages/deploy-diff.page.ts
index 47d70e408..1a921a409 100644
--- a/src/Web/StellaOps.Web/src/app/features/deploy-diff/pages/deploy-diff.page.ts
+++ b/src/Web/StellaOps.Web/src/app/features/deploy-diff/pages/deploy-diff.page.ts
@@ -101,23 +101,23 @@ import { DeployAction, SignerIdentity } from '../models/deploy-diff.models';
font-size: 0.875rem;
&--current {
- color: var(--text-primary);
- font-weight: 500;
+ color: var(--color-text-primary);
+ font-weight: var(--font-weight-medium);
}
}
.breadcrumb-link {
- color: var(--text-secondary);
+ color: var(--color-text-secondary);
text-decoration: none;
&:hover {
- color: var(--text-link);
+ color: var(--color-brand-primary);
text-decoration: underline;
}
}
.breadcrumb-separator {
- color: var(--text-muted);
+ color: var(--color-text-muted);
}
/* Invalid params state */
@@ -132,48 +132,48 @@ import { DeployAction, SignerIdentity } from '../models/deploy-diff.models';
flex: 1;
svg {
- color: var(--text-muted);
+ color: var(--color-text-muted);
opacity: 0.5;
}
h2 {
margin: 0;
font-size: 1.25rem;
- font-weight: 600;
- color: var(--text-primary);
+ font-weight: var(--font-weight-semibold);
+ color: var(--color-text-primary);
}
p {
margin: 0;
font-size: 0.875rem;
- color: var(--text-secondary);
+ color: var(--color-text-secondary);
max-width: 400px;
code {
padding: 0.125rem 0.375rem;
font-family: 'SF Mono', 'Consolas', monospace;
font-size: 0.8125rem;
- background: var(--surface-secondary);
- border-radius: 4px;
+ background: var(--color-surface-secondary);
+ border-radius: var(--radius-sm);
}
}
}
.example-url {
padding: 1rem;
- background: var(--surface-secondary);
- border-radius: 8px;
+ background: var(--color-surface-secondary);
+ border-radius: var(--radius-lg);
font-size: 0.8125rem;
strong {
- color: var(--text-secondary);
+ color: var(--color-text-secondary);
}
code {
display: block;
margin-top: 0.5rem;
font-family: 'SF Mono', 'Consolas', monospace;
- color: var(--text-primary);
+ color: var(--color-text-primary);
word-break: break-all;
}
}
@@ -184,11 +184,11 @@ import { DeployAction, SignerIdentity } from '../models/deploy-diff.models';
gap: 0.5rem;
padding: 0.5rem 1rem;
font-size: 0.875rem;
- font-weight: 500;
- color: var(--text-link);
+ font-weight: var(--font-weight-medium);
+ color: var(--color-brand-primary);
text-decoration: none;
border: 1px solid var(--color-primary);
- border-radius: 6px;
+ border-radius: var(--radius-md);
transition: all 0.15s;
&:hover {
diff --git a/src/Web/StellaOps.Web/src/app/features/deployments/deployment-detail-page.component.ts b/src/Web/StellaOps.Web/src/app/features/deployments/deployment-detail-page.component.ts
index 76f66f3fc..e920daaf6 100644
--- a/src/Web/StellaOps.Web/src/app/features/deployments/deployment-detail-page.component.ts
+++ b/src/Web/StellaOps.Web/src/app/features/deployments/deployment-detail-page.component.ts
@@ -102,11 +102,11 @@ interface DeploymentArtifact {
>
@switch (step.status) {
- @case ('complete') {
✓ }
- @case ('running') {
⟳ }
- @case ('pending') {
○ }
- @case ('failed') {
✕ }
- @case ('skipped') {
⊘ }
+ @case ('complete') {
}
+ @case ('running') {
}
+ @case ('pending') {
}
+ @case ('failed') {
}
+ @case ('skipped') {
}
}
@@ -170,11 +170,11 @@ interface DeploymentArtifact {
@switch (artifact.type) {
- @case ('lock') { 🔒 }
- @case ('script') { 📜 }
- @case ('evidence') { 📋 }
- @case ('manifest') { 📄 }
- @case ('config') { ⚙️ }
+ @case ('lock') { }
+ @case ('script') { }
+ @case ('evidence') { }
+ @case ('manifest') { }
+ @case ('config') { }
}
{{ artifact.name }}
@@ -187,7 +187,7 @@ interface DeploymentArtifact {
{{ artifact.hash.slice(0, 16) }}...
- 📋
+
{{ artifact.size }}
@@ -231,7 +231,11 @@ interface DeploymentArtifact {
(click)="toggleAutoScroll()"
title="Auto-scroll"
>
- {{ autoScroll() ? '⏸ Pause' : '▶ Follow' }}
+ @if (autoScroll()) {
+
Pause
+ } @else {
+
Follow
+ }
Download
@@ -318,70 +322,70 @@ interface DeploymentArtifact {
/* Header */
.page-header { margin-bottom: 1.5rem; display: flex; flex-wrap: wrap; justify-content: space-between; align-items: flex-start; gap: 1rem; }
- .back-link { display: block; margin-bottom: 0.5rem; font-size: 0.875rem; color: var(--primary-color); text-decoration: none; width: 100%; }
+ .back-link { display: block; margin-bottom: 0.5rem; font-size: 0.875rem; color: var(--color-brand-primary); text-decoration: none; width: 100%; }
.header-main { flex: 1; }
.header-title-row { display: flex; align-items: center; gap: 0.75rem; margin-bottom: 0.5rem; }
- .page-title { margin: 0; font-size: 1.5rem; font-weight: 600; font-family: ui-monospace, SFMono-Regular, monospace; }
- .header-meta { display: flex; flex-wrap: wrap; gap: 1rem; font-size: 0.875rem; color: var(--text-color-secondary); }
- .header-meta strong { color: var(--text-color); }
+ .page-title { margin: 0; font-size: 1.5rem; font-weight: var(--font-weight-semibold); font-family: ui-monospace, SFMono-Regular, monospace; }
+ .header-meta { display: flex; flex-wrap: wrap; gap: 1rem; font-size: 0.875rem; color: var(--color-text-secondary); }
+ .header-meta strong { color: var(--color-text-primary); }
.header-meta code { font-size: 0.625rem; }
.header-actions { display: flex; gap: 0.5rem; flex-wrap: wrap; }
- .status-badge { padding: 0.25rem 0.75rem; border-radius: 4px; font-size: 0.75rem; font-weight: 600; }
- .status-badge--running { background: var(--blue-100); color: var(--blue-700); }
- .status-badge--success { background: var(--green-100); color: var(--green-700); }
- .status-badge--failed { background: var(--red-100); color: var(--red-700); }
- .status-badge--cancelled { background: var(--gray-100); color: var(--gray-600); }
+ .status-badge { padding: 0.25rem 0.75rem; border-radius: var(--radius-sm); font-size: 0.75rem; font-weight: var(--font-weight-semibold); }
+ .status-badge--running { background: var(--color-severity-info-bg); color: var(--color-status-info-text); }
+ .status-badge--success { background: var(--color-severity-low-bg); color: var(--color-status-success-text); }
+ .status-badge--failed { background: var(--color-severity-critical-bg); color: var(--color-status-error-text); }
+ .status-badge--cancelled { background: var(--color-severity-none-bg); color: var(--color-text-secondary); }
/* Tabs */
- .tabs { display: flex; gap: 0.25rem; border-bottom: 1px solid var(--surface-border); margin-bottom: 1.5rem; overflow-x: auto; }
- .tab { padding: 0.75rem 1rem; background: transparent; border: none; border-bottom: 2px solid transparent; margin-bottom: -1px; font-size: 0.875rem; font-weight: 500; color: var(--text-color-secondary); cursor: pointer; white-space: nowrap; }
- .tab:hover { color: var(--text-color); }
- .tab--active { color: var(--primary-color); border-bottom-color: var(--primary-color); }
+ .tabs { display: flex; gap: 0.25rem; border-bottom: 1px solid var(--color-border-primary); margin-bottom: 1.5rem; overflow-x: auto; }
+ .tab { padding: 0.75rem 1rem; background: transparent; border: none; border-bottom: 2px solid transparent; margin-bottom: -1px; font-size: 0.875rem; font-weight: var(--font-weight-medium); color: var(--color-text-secondary); cursor: pointer; white-space: nowrap; }
+ .tab:hover { color: var(--color-text-primary); }
+ .tab--active { color: var(--color-brand-primary); border-bottom-color: var(--color-brand-primary); }
/* Panel */
- .panel { padding: 1.25rem; background: var(--surface-card); border: 1px solid var(--surface-border); border-radius: 8px; }
- .panel h3 { margin: 0; font-size: 1rem; font-weight: 600; }
+ .panel { padding: 1.25rem; background: var(--color-surface-primary); border: 1px solid var(--color-border-primary); border-radius: var(--radius-lg); }
+ .panel h3 { margin: 0; font-size: 1rem; font-weight: var(--font-weight-semibold); }
.panel-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem; flex-wrap: wrap; gap: 0.5rem; }
/* Workflow DAG (DEP-003) */
.workflow-panel { }
- .workflow-summary { font-size: 0.75rem; color: var(--text-color-secondary); }
+ .workflow-summary { font-size: 0.75rem; color: var(--color-text-secondary); }
.workflow-dag { display: flex; flex-direction: column; align-items: center; gap: 0; padding: 1rem 0; }
- .dag-node { display: flex; align-items: center; gap: 1rem; padding: 0.75rem 1.5rem; background: var(--surface-ground); border: 2px solid var(--surface-border); border-radius: 8px; cursor: pointer; transition: all 0.15s; min-width: 250px; position: relative; }
- .dag-node:hover { border-color: var(--primary-color); }
- .dag-node--selected { border-color: var(--primary-color); box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2); }
- .dag-node--complete { border-color: var(--green-300); background: var(--green-50); }
- .dag-node--running { border-color: var(--blue-300); background: var(--blue-50); animation: pulse 2s infinite; }
- .dag-node--failed { border-color: var(--red-300); background: var(--red-50); }
- .dag-node--pending { border-color: var(--gray-200); background: var(--gray-50); }
- .dag-node--skipped { border-color: var(--gray-200); background: var(--gray-100); opacity: 0.6; }
- .dag-node__icon { width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; border-radius: 50%; font-size: 1rem; }
- .dag-node--complete .dag-node__icon { background: var(--green-100); color: var(--green-600); }
- .dag-node--running .dag-node__icon { background: var(--blue-100); color: var(--blue-600); }
- .dag-node--failed .dag-node__icon { background: var(--red-100); color: var(--red-600); }
- .dag-node--pending .dag-node__icon { background: var(--gray-100); color: var(--gray-400); }
+ .dag-node { display: flex; align-items: center; gap: 1rem; padding: 0.75rem 1.5rem; background: var(--color-surface-secondary); border: 2px solid var(--color-border-primary); border-radius: var(--radius-lg); cursor: pointer; transition: all 0.15s; min-width: 250px; position: relative; }
+ .dag-node:hover { border-color: var(--color-brand-primary); }
+ .dag-node--selected { border-color: var(--color-brand-primary); box-shadow: 0 0 0 3px var(--color-focus-ring); }
+ .dag-node--complete { border-color: var(--color-severity-low-border); background: var(--color-severity-low-bg); }
+ .dag-node--running { border-color: var(--color-severity-info-border); background: var(--color-severity-info-bg); animation: pulse 2s infinite; }
+ .dag-node--failed { border-color: var(--color-severity-critical-border); background: var(--color-severity-critical-bg); }
+ .dag-node--pending { border-color: var(--color-border-primary); background: var(--color-severity-none-bg); }
+ .dag-node--skipped { border-color: var(--color-border-primary); background: var(--color-severity-none-bg); opacity: 0.6; }
+ .dag-node__icon { width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; border-radius: var(--radius-full); font-size: 1rem; }
+ .dag-node--complete .dag-node__icon { background: var(--color-severity-low-bg); color: var(--color-status-success-text); }
+ .dag-node--running .dag-node__icon { background: var(--color-severity-info-bg); color: var(--color-status-info-text); }
+ .dag-node--failed .dag-node__icon { background: var(--color-severity-critical-bg); color: var(--color-status-error-text); }
+ .dag-node--pending .dag-node__icon { background: var(--color-severity-none-bg); color: var(--color-text-muted); }
.dag-node__content { flex: 1; }
- .dag-node__name { display: block; font-weight: 600; font-size: 0.875rem; }
- .dag-node__duration { display: block; font-size: 0.75rem; color: var(--text-color-secondary); }
- .dag-connector { position: absolute; bottom: -40px; left: 50%; transform: translateX(-50%); color: var(--surface-border); z-index: 1; }
+ .dag-node__name { display: block; font-weight: var(--font-weight-semibold); font-size: 0.875rem; }
+ .dag-node__duration { display: block; font-size: 0.75rem; color: var(--color-text-secondary); }
+ .dag-connector { position: absolute; bottom: -40px; left: 50%; transform: translateX(-50%); color: var(--color-border-primary); z-index: 1; }
.icon-spin { animation: spin 1s linear infinite; }
@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.7; } }
- .step-detail { margin-top: 1.5rem; padding: 1rem; background: var(--surface-ground); border-radius: 8px; border: 1px solid var(--surface-border); }
+ .step-detail { margin-top: 1.5rem; padding: 1rem; background: var(--color-surface-secondary); border-radius: var(--radius-lg); border: 1px solid var(--color-border-primary); }
.step-detail__header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.5rem; }
.step-detail__header h4 { margin: 0; font-size: 0.875rem; }
- .step-detail__status { font-size: 0.625rem; padding: 0.125rem 0.5rem; border-radius: 4px; font-weight: 600; }
- .step-detail__status--complete { background: var(--green-100); color: var(--green-700); }
- .step-detail__status--running { background: var(--blue-100); color: var(--blue-700); }
- .step-detail__status--failed { background: var(--red-100); color: var(--red-700); }
- .step-detail__meta { font-size: 0.75rem; color: var(--text-color-secondary); display: flex; gap: 1rem; margin-bottom: 0.75rem; }
- .step-detail__logs { background: var(--gray-900); padding: 0.75rem; border-radius: 6px; max-height: 200px; overflow: auto; }
- .step-detail__logs pre { margin: 0; font-size: 0.625rem; color: var(--gray-100); font-family: ui-monospace, monospace; }
+ .step-detail__status { font-size: 0.625rem; padding: 0.125rem 0.5rem; border-radius: var(--radius-sm); font-weight: var(--font-weight-semibold); }
+ .step-detail__status--complete { background: var(--color-severity-low-bg); color: var(--color-status-success-text); }
+ .step-detail__status--running { background: var(--color-severity-info-bg); color: var(--color-status-info-text); }
+ .step-detail__status--failed { background: var(--color-severity-critical-bg); color: var(--color-status-error-text); }
+ .step-detail__meta { font-size: 0.75rem; color: var(--color-text-secondary); display: flex; gap: 1rem; margin-bottom: 0.75rem; }
+ .step-detail__logs { background: var(--color-text-heading); padding: 0.75rem; border-radius: var(--radius-md); max-height: 200px; overflow: auto; }
+ .step-detail__logs pre { margin: 0; font-size: 0.625rem; color: var(--color-severity-none-bg); font-family: ui-monospace, monospace; }
/* Artifacts Table (DEP-004) */
- .artifacts-count { font-size: 0.75rem; color: var(--text-color-secondary); }
+ .artifacts-count { font-size: 0.75rem; color: var(--color-text-secondary); }
.artifacts-table { }
.artifact-name { display: flex; align-items: center; gap: 0.5rem; }
.artifact-icon { font-size: 1rem; }
@@ -389,52 +393,52 @@ interface DeploymentArtifact {
.artifact-hash code { font-size: 0.625rem; }
.copy-btn { background: none; border: none; cursor: pointer; padding: 0.25rem; font-size: 0.75rem; }
.artifact-actions { display: flex; gap: 0.5rem; }
- .type-badge { font-size: 0.625rem; padding: 0.125rem 0.5rem; border-radius: 4px; font-weight: 500; text-transform: uppercase; }
- .type-badge--lock { background: var(--blue-100); color: var(--blue-700); }
- .type-badge--script { background: var(--purple-100); color: var(--purple-700); }
- .type-badge--evidence { background: var(--green-100); color: var(--green-700); }
- .type-badge--manifest { background: var(--yellow-100); color: var(--yellow-700); }
- .type-badge--config { background: var(--gray-100); color: var(--gray-600); }
+ .type-badge { font-size: 0.625rem; padding: 0.125rem 0.5rem; border-radius: var(--radius-sm); font-weight: var(--font-weight-medium); text-transform: uppercase; }
+ .type-badge--lock { background: var(--color-severity-info-bg); color: var(--color-status-info-text); }
+ .type-badge--script { background: var(--color-status-excepted-bg); color: var(--color-status-excepted); }
+ .type-badge--evidence { background: var(--color-severity-low-bg); color: var(--color-status-success-text); }
+ .type-badge--manifest { background: var(--color-severity-medium-bg); color: var(--color-status-warning-text); }
+ .type-badge--config { background: var(--color-severity-none-bg); color: var(--color-text-secondary); }
/* Logs (DEP-005) */
.logs-panel { display: flex; flex-direction: column; }
.logs-controls { display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: center; }
- .logs-step-select { padding: 0.375rem 0.75rem; border: 1px solid var(--surface-border); border-radius: 6px; font-size: 0.75rem; }
- .logs-search { padding: 0.375rem 0.75rem; border: 1px solid var(--surface-border); border-radius: 6px; font-size: 0.75rem; width: 200px; }
- .log-viewer { background: var(--gray-900); border-radius: 8px; padding: 1rem; max-height: 500px; overflow: auto; flex: 1; }
- .log-content { margin: 0; font-size: 0.75rem; color: var(--gray-100); font-family: ui-monospace, monospace; white-space: pre-wrap; }
- .logs-footer { display: flex; justify-content: space-between; padding-top: 0.5rem; font-size: 0.75rem; color: var(--text-color-secondary); }
- .logs-match-count { color: var(--yellow-600); }
+ .logs-step-select { padding: 0.375rem 0.75rem; border: 1px solid var(--color-border-primary); border-radius: var(--radius-md); font-size: 0.75rem; }
+ .logs-search { padding: 0.375rem 0.75rem; border: 1px solid var(--color-border-primary); border-radius: var(--radius-md); font-size: 0.75rem; width: 200px; }
+ .log-viewer { background: var(--color-text-heading); border-radius: var(--radius-lg); padding: 1rem; max-height: 500px; overflow: auto; flex: 1; }
+ .log-content { margin: 0; font-size: 0.75rem; color: var(--color-severity-none-bg); font-family: ui-monospace, monospace; white-space: pre-wrap; }
+ .logs-footer { display: flex; justify-content: space-between; padding-top: 0.5rem; font-size: 0.75rem; color: var(--color-text-secondary); }
+ .logs-match-count { color: var(--color-status-warning-text); }
/* Targets */
- .target-status { font-size: 0.625rem; padding: 0.125rem 0.5rem; border-radius: 4px; font-weight: 600; }
- .target-status--ok { background: var(--green-100); color: var(--green-700); }
- .target-status--failed { background: var(--red-100); color: var(--red-700); }
- .target-status--pending { background: var(--yellow-100); color: var(--yellow-700); }
+ .target-status { font-size: 0.625rem; padding: 0.125rem 0.5rem; border-radius: var(--radius-sm); font-weight: var(--font-weight-semibold); }
+ .target-status--ok { background: var(--color-severity-low-bg); color: var(--color-status-success-text); }
+ .target-status--failed { background: var(--color-severity-critical-bg); color: var(--color-status-error-text); }
+ .target-status--pending { background: var(--color-severity-medium-bg); color: var(--color-status-warning-text); }
/* Evidence */
.evidence-info { margin: 0 0 1rem; font-size: 0.875rem; }
- .evidence-info a { color: var(--primary-color); }
+ .evidence-info a { color: var(--color-brand-primary); }
.evidence-summary { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1rem; }
.evidence-item { display: flex; flex-direction: column; gap: 0.25rem; }
- .evidence-label { font-size: 0.625rem; text-transform: uppercase; color: var(--text-color-secondary); }
+ .evidence-label { font-size: 0.625rem; text-transform: uppercase; color: var(--color-text-secondary); }
.evidence-item code { font-size: 0.625rem; }
- .evidence-badge { font-size: 0.625rem; padding: 0.125rem 0.5rem; border-radius: 4px; font-weight: 600; width: fit-content; }
- .evidence-badge--success { background: var(--green-100); color: var(--green-700); }
- .rekor-link { font-size: 0.875rem; color: var(--primary-color); }
+ .evidence-badge { font-size: 0.625rem; padding: 0.125rem 0.5rem; border-radius: var(--radius-sm); font-weight: var(--font-weight-semibold); width: fit-content; }
+ .evidence-badge--success { background: var(--color-severity-low-bg); color: var(--color-status-success-text); }
+ .rekor-link { font-size: 0.875rem; color: var(--color-brand-primary); }
/* Data Table */
.data-table { width: 100%; border-collapse: collapse; }
- .data-table th, .data-table td { padding: 0.75rem; text-align: left; border-bottom: 1px solid var(--surface-border); }
- .data-table th { font-size: 0.75rem; font-weight: 600; color: var(--text-color-secondary); text-transform: uppercase; }
+ .data-table th, .data-table td { padding: 0.75rem; text-align: left; border-bottom: 1px solid var(--color-border-primary); }
+ .data-table th { font-size: 0.75rem; font-weight: var(--font-weight-semibold); color: var(--color-text-secondary); text-transform: uppercase; }
.data-table code { font-size: 0.625rem; }
/* Buttons */
- .btn { padding: 0.5rem 1rem; border-radius: 6px; font-size: 0.875rem; font-weight: 500; cursor: pointer; text-decoration: none; }
+ .btn { padding: 0.5rem 1rem; border-radius: var(--radius-md); font-size: 0.875rem; font-weight: var(--font-weight-medium); cursor: pointer; text-decoration: none; }
.btn--sm { padding: 0.25rem 0.5rem; font-size: 0.75rem; }
- .btn--secondary { background: var(--surface-ground); border: 1px solid var(--surface-border); color: var(--text-color); }
- .btn--secondary:hover { background: var(--surface-hover); }
- .btn--secondary.active { background: var(--primary-color); color: white; border-color: var(--primary-color); }
+ .btn--secondary { background: var(--color-surface-secondary); border: 1px solid var(--color-border-primary); color: var(--color-text-primary); }
+ .btn--secondary:hover { background: var(--color-nav-hover); }
+ .btn--secondary.active { background: var(--color-brand-primary); color: white; border-color: var(--color-brand-primary); }
@media (max-width: 768px) {
.evidence-summary { grid-template-columns: 1fr; }
diff --git a/src/Web/StellaOps.Web/src/app/features/deployments/deployments-list-page.component.ts b/src/Web/StellaOps.Web/src/app/features/deployments/deployments-list-page.component.ts
index f84814e92..51da98a7f 100644
--- a/src/Web/StellaOps.Web/src/app/features/deployments/deployments-list-page.component.ts
+++ b/src/Web/StellaOps.Web/src/app/features/deployments/deployments-list-page.component.ts
@@ -81,40 +81,40 @@ interface Deployment {
styles: [`
.deployments-page { max-width: 1400px; margin: 0 auto; }
.page-header { margin-bottom: 1.5rem; }
- .page-title { margin: 0 0 0.25rem; font-size: 1.5rem; font-weight: 600; }
- .page-subtitle { margin: 0; color: var(--text-color-secondary); }
+ .page-title { margin: 0 0 0.25rem; font-size: 1.5rem; font-weight: var(--font-weight-semibold); }
+ .page-subtitle { margin: 0; color: var(--color-text-secondary); }
.table-container {
- background: var(--surface-card);
- border: 1px solid var(--surface-border);
- border-radius: 8px;
+ background: var(--color-surface-primary);
+ border: 1px solid var(--color-border-primary);
+ border-radius: var(--radius-lg);
overflow: hidden;
}
.data-table { width: 100%; border-collapse: collapse; }
- .data-table th, .data-table td { padding: 0.75rem 1rem; text-align: left; border-bottom: 1px solid var(--surface-border); }
- .data-table th { background: var(--surface-ground); font-size: 0.75rem; font-weight: 600; color: var(--text-color-secondary); text-transform: uppercase; }
- .data-table tbody tr:hover { background: var(--surface-hover); }
- .data-table a { color: var(--primary-color); text-decoration: none; }
+ .data-table th, .data-table td { padding: 0.75rem 1rem; text-align: left; border-bottom: 1px solid var(--color-border-primary); }
+ .data-table th { background: var(--color-surface-secondary); font-size: 0.75rem; font-weight: var(--font-weight-semibold); color: var(--color-text-secondary); text-transform: uppercase; }
+ .data-table tbody tr:hover { background: var(--color-nav-hover); }
+ .data-table a { color: var(--color-brand-primary); text-decoration: none; }
- .deployment-link { font-family: ui-monospace, SFMono-Regular, monospace; font-weight: 500; }
+ .deployment-link { font-family: ui-monospace, SFMono-Regular, monospace; font-weight: var(--font-weight-medium); }
- .status-badge { display: inline-flex; align-items: center; gap: 0.375rem; padding: 0.125rem 0.5rem; border-radius: 4px; font-size: 0.625rem; font-weight: 600; }
- .status-badge--running { background: var(--blue-100); color: var(--blue-700); }
- .status-badge--success { background: var(--green-100); color: var(--green-700); }
- .status-badge--failed { background: var(--red-100); color: var(--red-700); }
- .status-badge--cancelled { background: var(--gray-100); color: var(--gray-600); }
+ .status-badge { display: inline-flex; align-items: center; gap: 0.375rem; padding: 0.125rem 0.5rem; border-radius: var(--radius-sm); font-size: 0.625rem; font-weight: var(--font-weight-semibold); }
+ .status-badge--running { background: var(--color-severity-info-bg); color: var(--color-status-info-text); }
+ .status-badge--success { background: var(--color-severity-low-bg); color: var(--color-status-success-text); }
+ .status-badge--failed { background: var(--color-severity-critical-bg); color: var(--color-status-error-text); }
+ .status-badge--cancelled { background: var(--color-severity-none-bg); color: var(--color-text-secondary); }
.spinner {
width: 10px;
height: 10px;
border: 2px solid currentColor;
border-top-color: transparent;
- border-radius: 50%;
+ border-radius: var(--radius-full);
animation: spin 1s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
- .btn { padding: 0.25rem 0.5rem; border-radius: 6px; font-size: 0.75rem; font-weight: 500; text-decoration: none; background: var(--surface-ground); border: 1px solid var(--surface-border); color: var(--text-color); }
+ .btn { padding: 0.25rem 0.5rem; border-radius: var(--radius-md); font-size: 0.75rem; font-weight: var(--font-weight-medium); text-decoration: none; background: var(--color-surface-secondary); border: 1px solid var(--color-border-primary); color: var(--color-text-primary); }
`]
})
export class DeploymentsListPageComponent {
diff --git a/src/Web/StellaOps.Web/src/app/features/doctor/components/check-result/check-result.component.html b/src/Web/StellaOps.Web/src/app/features/doctor/components/check-result/check-result.component.html
index 75caa4af6..56075bc71 100644
--- a/src/Web/StellaOps.Web/src/app/features/doctor/components/check-result/check-result.component.html
+++ b/src/Web/StellaOps.Web/src/app/features/doctor/components/check-result/check-result.component.html
@@ -14,9 +14,9 @@
- ↻
+
-
{{ expanded ? '▲' : '▼' }}
+
diff --git a/src/Web/StellaOps.Web/src/app/features/doctor/components/check-result/check-result.component.ts b/src/Web/StellaOps.Web/src/app/features/doctor/components/check-result/check-result.component.ts
index cee391c8a..b085e75d7 100644
--- a/src/Web/StellaOps.Web/src/app/features/doctor/components/check-result/check-result.component.ts
+++ b/src/Web/StellaOps.Web/src/app/features/doctor/components/check-result/check-result.component.ts
@@ -17,6 +17,11 @@ export class CheckResultComponent {
@Input() fixEnabled = false;
@Output() rerun = new EventEmitter();
+ private readonly svgAttrs = 'xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"';
+
+ readonly chevronUpSvg = ` `;
+ readonly chevronDownSvg = ` `;
+
get severityClass(): string {
return `severity-${this.result.severity}`;
}
@@ -24,17 +29,17 @@ export class CheckResultComponent {
get severityIcon(): string {
switch (this.result.severity) {
case 'pass':
- return '✔'; // checkmark
+ return ` `;
case 'info':
- return 'ℹ'; // info
+ return ` `;
case 'warn':
- return '⚠'; // warning triangle
+ return ` `;
case 'fail':
- return '✘'; // x mark
+ return ` `;
case 'skip':
- return '→'; // arrow right
+ return ` `;
default:
- return '?'; // question mark
+ return ` `;
}
}
diff --git a/src/Web/StellaOps.Web/src/app/features/doctor/components/evidence-viewer/evidence-viewer.component.ts b/src/Web/StellaOps.Web/src/app/features/doctor/components/evidence-viewer/evidence-viewer.component.ts
index 1bcc0c932..c7b5d72aa 100644
--- a/src/Web/StellaOps.Web/src/app/features/doctor/components/evidence-viewer/evidence-viewer.component.ts
+++ b/src/Web/StellaOps.Web/src/app/features/doctor/components/evidence-viewer/evidence-viewer.component.ts
@@ -10,7 +10,7 @@ import { Evidence } from '../../models/doctor.models';
@if (expanded()) {
@@ -39,8 +39,8 @@ import { Evidence } from '../../models/doctor.models';
`,
styles: [`
.evidence-viewer {
- background: var(--bg-tertiary);
- border-radius: 6px;
+ background: var(--color-surface-tertiary);
+ border-radius: var(--radius-md);
margin-bottom: 1rem;
}
@@ -50,23 +50,23 @@ import { Evidence } from '../../models/doctor.models';
align-items: center;
padding: 0.75rem;
cursor: pointer;
- border-radius: 6px;
+ border-radius: var(--radius-md);
transition: background 0.15s ease;
&:hover {
- background: var(--bg-hover);
+ background: var(--color-nav-hover);
}
h4 {
margin: 0;
font-size: 0.875rem;
- font-weight: 600;
- color: var(--text-primary);
+ font-weight: var(--font-weight-semibold);
+ color: var(--color-text-primary);
}
.toggle-icon {
font-size: 0.75rem;
- color: var(--text-tertiary);
+ color: var(--color-text-muted);
}
}
@@ -77,13 +77,13 @@ import { Evidence } from '../../models/doctor.models';
.evidence-description {
margin: 0 0 0.75rem 0;
font-size: 0.875rem;
- color: var(--text-secondary);
+ color: var(--color-text-secondary);
}
.evidence-data {
background: white;
- border: 1px solid var(--border);
- border-radius: 6px;
+ border: 1px solid var(--color-border-primary);
+ border-radius: var(--radius-md);
overflow: hidden;
table {
@@ -92,7 +92,7 @@ import { Evidence } from '../../models/doctor.models';
}
tr:not(:last-child) {
- border-bottom: 1px solid var(--border);
+ border-bottom: 1px solid var(--color-border-primary);
}
td {
@@ -102,17 +102,17 @@ import { Evidence } from '../../models/doctor.models';
.data-key {
width: 30%;
- font-weight: 500;
- color: var(--text-primary);
- background: var(--bg-secondary);
- border-right: 1px solid var(--border);
+ font-weight: var(--font-weight-medium);
+ color: var(--color-text-primary);
+ background: var(--color-surface-secondary);
+ border-right: 1px solid var(--color-border-primary);
}
.data-value {
code {
font-family: 'JetBrains Mono', 'Fira Code', monospace;
font-size: 0.75rem;
- color: var(--text-secondary);
+ color: var(--color-text-secondary);
word-break: break-all;
}
}
@@ -122,6 +122,10 @@ import { Evidence } from '../../models/doctor.models';
export class EvidenceViewerComponent {
@Input({ required: true }) evidence!: Evidence;
+ private readonly svgAttrs = 'xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"';
+ readonly chevronDownSvg = `
`;
+ readonly chevronRightSvg = `
`;
+
readonly expanded = signal(false);
toggleExpanded(): void {
diff --git a/src/Web/StellaOps.Web/src/app/features/doctor/components/export-dialog/export-dialog.component.ts b/src/Web/StellaOps.Web/src/app/features/doctor/components/export-dialog/export-dialog.component.ts
index db3563971..304043db8 100644
--- a/src/Web/StellaOps.Web/src/app/features/doctor/components/export-dialog/export-dialog.component.ts
+++ b/src/Web/StellaOps.Web/src/app/features/doctor/components/export-dialog/export-dialog.component.ts
@@ -103,7 +103,7 @@ type ExportFormat = 'json' | 'markdown' | 'text' | 'dsse';
.dialog {
background: white;
- border-radius: 12px;
+ border-radius: var(--radius-xl);
width: 100%;
max-width: 600px;
max-height: 90vh;
@@ -117,12 +117,12 @@ type ExportFormat = 'json' | 'markdown' | 'text' | 'dsse';
justify-content: space-between;
align-items: center;
padding: 1rem 1.5rem;
- border-bottom: 1px solid var(--border);
+ border-bottom: 1px solid var(--color-border-primary);
h2 {
margin: 0;
font-size: 1.25rem;
- font-weight: 600;
+ font-weight: var(--font-weight-semibold);
}
.close-btn {
@@ -131,15 +131,15 @@ type ExportFormat = 'json' | 'markdown' | 'text' | 'dsse';
border: none;
background: transparent;
font-size: 1.5rem;
- color: var(--text-secondary);
+ color: var(--color-text-secondary);
cursor: pointer;
- border-radius: 4px;
+ border-radius: var(--radius-sm);
display: flex;
align-items: center;
justify-content: center;
&:hover {
- background: var(--bg-hover);
+ background: var(--color-nav-hover);
}
}
}
@@ -162,18 +162,18 @@ type ExportFormat = 'json' | 'markdown' | 'text' | 'dsse';
align-items: flex-start;
gap: 0.75rem;
padding: 0.75rem;
- border: 1px solid var(--border);
- border-radius: 8px;
+ border: 1px solid var(--color-border-primary);
+ border-radius: var(--radius-lg);
cursor: pointer;
transition: all 0.15s ease;
&:hover {
- background: var(--bg-hover);
+ background: var(--color-nav-hover);
}
&:has(input:checked) {
- border-color: var(--primary);
- background: var(--primary-light);
+ border-color: var(--color-brand-primary);
+ background: var(--color-brand-light);
}
input {
@@ -186,12 +186,12 @@ type ExportFormat = 'json' | 'markdown' | 'text' | 'dsse';
strong {
font-size: 0.875rem;
- color: var(--text-primary);
+ color: var(--color-text-primary);
}
small {
font-size: 0.75rem;
- color: var(--text-secondary);
+ color: var(--color-text-secondary);
}
}
}
@@ -205,11 +205,11 @@ type ExportFormat = 'json' | 'markdown' | 'text' | 'dsse';
.dsse-note {
padding: 0.75rem;
- border: 1px dashed var(--border);
- border-radius: 8px;
+ border: 1px dashed var(--color-border-primary);
+ border-radius: var(--radius-lg);
margin-bottom: 1.5rem;
font-size: 0.8125rem;
- color: var(--text-secondary);
+ color: var(--color-text-secondary);
}
.checkbox-option {
@@ -217,7 +217,7 @@ type ExportFormat = 'json' | 'markdown' | 'text' | 'dsse';
align-items: center;
gap: 0.5rem;
font-size: 0.875rem;
- color: var(--text-primary);
+ color: var(--color-text-primary);
cursor: pointer;
input {
@@ -230,19 +230,19 @@ type ExportFormat = 'json' | 'markdown' | 'text' | 'dsse';
h4 {
margin: 0 0 0.5rem 0;
font-size: 0.875rem;
- font-weight: 600;
- color: var(--text-primary);
+ font-weight: var(--font-weight-semibold);
+ color: var(--color-text-primary);
}
}
.preview-content {
margin: 0;
padding: 1rem;
- background: var(--bg-code);
- border-radius: 8px;
+ background: var(--color-surface-tertiary);
+ border-radius: var(--radius-lg);
font-family: 'JetBrains Mono', 'Fira Code', monospace;
font-size: 0.75rem;
- color: var(--text-code);
+ color: var(--color-text-primary);
max-height: 200px;
overflow: auto;
white-space: pre-wrap;
@@ -254,35 +254,35 @@ type ExportFormat = 'json' | 'markdown' | 'text' | 'dsse';
justify-content: flex-end;
gap: 0.75rem;
padding: 1rem 1.5rem;
- border-top: 1px solid var(--border);
+ border-top: 1px solid var(--color-border-primary);
}
.btn {
padding: 0.5rem 1rem;
- border-radius: 6px;
+ border-radius: var(--radius-md);
font-size: 0.875rem;
- font-weight: 500;
+ font-weight: var(--font-weight-medium);
cursor: pointer;
transition: all 0.15s ease;
}
.btn-outline {
background: transparent;
- border: 1px solid var(--border);
- color: var(--text-primary);
+ border: 1px solid var(--color-border-primary);
+ color: var(--color-text-primary);
&:hover {
- background: var(--bg-hover);
+ background: var(--color-nav-hover);
}
}
.btn-primary {
- background: var(--primary);
- border: 1px solid var(--primary);
+ background: var(--color-brand-primary);
+ border: 1px solid var(--color-brand-primary);
color: var(--color-text-heading);
&:hover {
- background: var(--primary-dark);
+ background: var(--color-brand-secondary);
}
}
`]
diff --git a/src/Web/StellaOps.Web/src/app/features/doctor/components/registry/registry-capability-matrix.component.ts b/src/Web/StellaOps.Web/src/app/features/doctor/components/registry/registry-capability-matrix.component.ts
index 7ecd906a4..99fde8eca 100644
--- a/src/Web/StellaOps.Web/src/app/features/doctor/components/registry/registry-capability-matrix.component.ts
+++ b/src/Web/StellaOps.Web/src/app/features/doctor/components/registry/registry-capability-matrix.component.ts
@@ -34,7 +34,7 @@ interface CapabilityDefinition {
@if (registries.length === 0) {
-
🔍
+
No registries configured. Run Doctor checks to detect registry capabilities.
} @else {
@@ -62,7 +62,7 @@ interface CapabilityDefinition {
{{ capability.name }}
- {{ isExpanded(capability.id) ? '▼' : '▶' }}
+
@if (isExpanded(capability.id)) {
{{ capability.description }}
@@ -85,19 +85,19 @@ interface CapabilityDefinition {
- ⚪
+
Partial
- ✘
+
Not Supported
@@ -267,7 +267,7 @@ interface CapabilityDefinition {
justify-content: center;
width: 24px;
height: 24px;
- border-radius: 50%;
+ border-radius: var(--radius-full);
font-size: var(--font-size-sm);
.status-supported & {
@@ -328,6 +328,10 @@ interface CapabilityDefinition {
export class RegistryCapabilityMatrixComponent {
@Input() registries: RegistryInstance[] = [];
+ private readonly svgAttrs = 'xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"';
+ readonly chevronDownSvg = ` `;
+ readonly chevronRightSvg = ` `;
+
private readonly expandedCapabilities = signal>(new Set());
/**
diff --git a/src/Web/StellaOps.Web/src/app/features/doctor/components/registry/registry-check-details.component.ts b/src/Web/StellaOps.Web/src/app/features/doctor/components/registry/registry-check-details.component.ts
index 590d8a143..bab08f8d8 100644
--- a/src/Web/StellaOps.Web/src/app/features/doctor/components/registry/registry-check-details.component.ts
+++ b/src/Web/StellaOps.Web/src/app/features/doctor/components/registry/registry-check-details.component.ts
@@ -27,7 +27,7 @@ import {
- ✕
+
@@ -62,7 +62,7 @@ import {
{{ check.diagnosis }}
{{ formatDuration(check.durationMs) }}
- {{ isCheckExpanded(check.checkId) ? '▲' : '▼' }}
+
@if (isCheckExpanded(check.checkId) && check.evidence) {
@@ -151,7 +151,7 @@ import {
.status-indicator {
width: 40px;
height: 40px;
- border-radius: 50%;
+ border-radius: var(--radius-full);
display: flex;
align-items: center;
justify-content: center;
@@ -483,6 +483,10 @@ export class RegistryCheckDetailsComponent {
@Input({ required: true }) registry!: RegistryInstance;
@Output() close = new EventEmitter();
+ private readonly svgAttrs = 'xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"';
+ readonly chevronUpSvg = ` `;
+ readonly chevronDownSvg = ` `;
+
readonly activeTab = signal<'checks' | 'capabilities'>('checks');
private readonly expandedChecks = signal>(new Set());
@@ -517,30 +521,30 @@ export class RegistryCheckDetailsComponent {
getSeverityIcon(severity: DoctorSeverity): string {
switch (severity) {
case 'pass':
- return '✔';
+ return ` `;
case 'info':
- return 'ℹ';
+ return ` `;
case 'warn':
- return '⚠';
+ return ` `;
case 'fail':
- return '✘';
+ return ` `;
case 'skip':
- return '→';
+ return ` `;
default:
- return '?';
+ return ` `;
}
}
getCapabilityIcon(status: string): string {
switch (status) {
case 'supported':
- return '✔';
+ return ` `;
case 'partial':
- return '⚪';
+ return ` `;
case 'unsupported':
- return '✘';
+ return ` `;
default:
- return '?';
+ return ` `;
}
}
diff --git a/src/Web/StellaOps.Web/src/app/features/doctor/components/registry/registry-checks-panel.component.ts b/src/Web/StellaOps.Web/src/app/features/doctor/components/registry/registry-checks-panel.component.ts
index 1f11feaf2..b5d9c1d15 100644
--- a/src/Web/StellaOps.Web/src/app/features/doctor/components/registry/registry-checks-panel.component.ts
+++ b/src/Web/StellaOps.Web/src/app/features/doctor/components/registry/registry-checks-panel.component.ts
@@ -53,7 +53,7 @@ import { RegistryCheckDetailsComponent } from './registry-check-details.componen
@if (!hasRegistryResults()) {
-
🔍
+
No Registry Checks Available
Run Doctor diagnostics to analyze registry connectivity and capabilities.
diff --git a/src/Web/StellaOps.Web/src/app/features/doctor/components/registry/registry-health-card.component.ts b/src/Web/StellaOps.Web/src/app/features/doctor/components/registry/registry-health-card.component.ts
index ba026416e..02deb5016 100644
--- a/src/Web/StellaOps.Web/src/app/features/doctor/components/registry/registry-health-card.component.ts
+++ b/src/Web/StellaOps.Web/src/app/features/doctor/components/registry/registry-health-card.component.ts
@@ -37,15 +37,15 @@ import {
- ✔
+
{{ supportedCount }}
- ⚪
+
{{ partialCount }}
- ✘
+
{{ unsupportedCount }}
@@ -109,7 +109,7 @@ import {
.status-indicator {
width: 32px;
height: 32px;
- border-radius: 50%;
+ border-radius: var(--radius-full);
display: flex;
align-items: center;
justify-content: center;
diff --git a/src/Web/StellaOps.Web/src/app/features/doctor/components/remediation-panel/remediation-panel.component.ts b/src/Web/StellaOps.Web/src/app/features/doctor/components/remediation-panel/remediation-panel.component.ts
index 07fc75649..03bc8452d 100644
--- a/src/Web/StellaOps.Web/src/app/features/doctor/components/remediation-panel/remediation-panel.component.ts
+++ b/src/Web/StellaOps.Web/src/app/features/doctor/components/remediation-panel/remediation-panel.component.ts
@@ -26,14 +26,14 @@ import { Remediation, RemediationStep } from '../../models/doctor.models';
@if (remediation.requiresBackup) {
-
⚠
+
Backup recommended before proceeding
}
@if (remediation.safetyNote) {
- ℹ
+
{{ remediation.safetyNote }}
}
@@ -72,8 +72,8 @@ import { Remediation, RemediationStep } from '../../models/doctor.models';
margin-top: 1rem;
padding: 1rem;
background: white;
- border: 1px solid var(--border);
- border-radius: 8px;
+ border: 1px solid var(--color-border-primary);
+ border-radius: var(--radius-lg);
}
.panel-header {
@@ -85,8 +85,8 @@ import { Remediation, RemediationStep } from '../../models/doctor.models';
h4 {
margin: 0;
font-size: 0.9375rem;
- font-weight: 600;
- color: var(--text-primary);
+ font-weight: var(--font-weight-semibold);
+ color: var(--color-text-primary);
}
}
@@ -95,16 +95,16 @@ import { Remediation, RemediationStep } from '../../models/doctor.models';
.run-fix-btn {
padding: 0.25rem 0.625rem;
font-size: 0.75rem;
- background: var(--bg-secondary);
- border: 1px solid var(--border);
- border-radius: 4px;
+ background: var(--color-surface-secondary);
+ border: 1px solid var(--color-border-primary);
+ border-radius: var(--radius-sm);
cursor: pointer;
- color: var(--text-secondary);
+ color: var(--color-text-secondary);
transition: all 0.15s ease;
&:hover {
- background: var(--bg-hover);
- color: var(--text-primary);
+ background: var(--color-nav-hover);
+ color: var(--color-text-primary);
}
}
@@ -115,18 +115,18 @@ import { Remediation, RemediationStep } from '../../models/doctor.models';
}
.run-fix-btn {
- background: var(--primary);
- border-color: var(--primary);
+ background: var(--color-brand-primary);
+ border-color: var(--color-brand-primary);
color: white;
&:hover:not(:disabled) {
- background: var(--primary-dark);
+ background: var(--color-brand-secondary);
}
&:disabled {
- background: var(--bg-secondary);
- border-color: var(--border);
- color: var(--text-secondary);
+ background: var(--color-surface-secondary);
+ border-color: var(--color-border-primary);
+ color: var(--color-text-secondary);
}
}
@@ -135,12 +135,12 @@ import { Remediation, RemediationStep } from '../../models/doctor.models';
align-items: center;
gap: 0.5rem;
padding: 0.75rem;
- background: var(--warning-bg);
- border: 1px solid var(--warning-border);
- border-radius: 6px;
+ background: var(--color-status-warning-bg);
+ border: 1px solid var(--color-status-warning-border);
+ border-radius: var(--radius-md);
margin-bottom: 1rem;
font-size: 0.875rem;
- color: var(--warning-dark);
+ color: var(--color-status-warning-text);
.warning-icon {
font-size: 1rem;
@@ -152,12 +152,12 @@ import { Remediation, RemediationStep } from '../../models/doctor.models';
align-items: flex-start;
gap: 0.5rem;
padding: 0.75rem;
- background: var(--info-bg);
- border: 1px solid var(--info-border);
- border-radius: 6px;
+ background: var(--color-status-info-bg);
+ border: 1px solid var(--color-status-info-border);
+ border-radius: var(--radius-md);
margin-bottom: 1rem;
font-size: 0.875rem;
- color: var(--info-dark);
+ color: var(--color-status-info-text);
.note-icon {
font-size: 1rem;
@@ -180,15 +180,15 @@ import { Remediation, RemediationStep } from '../../models/doctor.models';
}
.step-number {
- font-weight: 600;
- color: var(--primary);
+ font-weight: var(--font-weight-semibold);
+ color: var(--color-brand-primary);
font-size: 0.875rem;
}
.step-description {
flex: 1;
font-size: 0.875rem;
- color: var(--text-primary);
+ color: var(--color-text-primary);
}
}
@@ -196,14 +196,14 @@ import { Remediation, RemediationStep } from '../../models/doctor.models';
.verification-command {
margin: 0;
padding: 0.75rem;
- background: var(--bg-code);
- border-radius: 6px;
+ background: var(--color-surface-tertiary);
+ border-radius: var(--radius-md);
overflow-x: auto;
code {
font-family: 'JetBrains Mono', 'Fira Code', monospace;
font-size: 0.8125rem;
- color: var(--text-code);
+ color: var(--color-text-primary);
white-space: pre-wrap;
word-break: break-all;
}
@@ -213,7 +213,7 @@ import { Remediation, RemediationStep } from '../../models/doctor.models';
display: inline-block;
margin-top: 0.25rem;
font-size: 0.6875rem;
- color: var(--text-tertiary);
+ color: var(--color-text-muted);
text-transform: uppercase;
letter-spacing: 0.025em;
}
@@ -221,7 +221,7 @@ import { Remediation, RemediationStep } from '../../models/doctor.models';
.verification-section {
margin-top: 1.5rem;
padding-top: 1rem;
- border-top: 1px solid var(--border);
+ border-top: 1px solid var(--color-border-primary);
.verification-header {
display: flex;
@@ -232,8 +232,8 @@ import { Remediation, RemediationStep } from '../../models/doctor.models';
h5 {
margin: 0;
font-size: 0.875rem;
- font-weight: 600;
- color: var(--text-primary);
+ font-weight: var(--font-weight-semibold);
+ color: var(--color-text-primary);
}
}
}
diff --git a/src/Web/StellaOps.Web/src/app/features/doctor/components/summary-strip/summary-strip.component.ts b/src/Web/StellaOps.Web/src/app/features/doctor/components/summary-strip/summary-strip.component.ts
index 1bf315280..b010176e7 100644
--- a/src/Web/StellaOps.Web/src/app/features/doctor/components/summary-strip/summary-strip.component.ts
+++ b/src/Web/StellaOps.Web/src/app/features/doctor/components/summary-strip/summary-strip.component.ts
@@ -47,19 +47,19 @@ import { DoctorSeverity, DoctorSummary } from '../../models/doctor.models';
align-items: center;
gap: 1.5rem;
padding: 1rem 1.5rem;
- background: var(--bg-secondary);
- border-radius: 8px;
+ background: var(--color-surface-secondary);
+ border-radius: var(--radius-lg);
margin-bottom: 1.5rem;
- border-left: 4px solid var(--success);
+ border-left: 4px solid var(--color-status-success);
&.overall-fail {
- border-left-color: var(--error);
- background: var(--error-bg);
+ border-left-color: var(--color-status-error);
+ background: var(--color-status-error-bg);
}
&.overall-warn {
- border-left-color: var(--warning);
- background: var(--warning-bg);
+ border-left-color: var(--color-status-warning);
+ background: var(--color-status-warning-bg);
}
}
@@ -71,26 +71,26 @@ import { DoctorSeverity, DoctorSummary } from '../../models/doctor.models';
.count {
font-size: 1.5rem;
- font-weight: 600;
+ font-weight: var(--font-weight-semibold);
line-height: 1;
}
.label {
font-size: 0.75rem;
- color: var(--text-secondary);
+ color: var(--color-text-secondary);
margin-top: 0.25rem;
text-transform: uppercase;
letter-spacing: 0.025em;
}
- &.passed .count { color: var(--success); }
- &.info .count { color: var(--info); }
- &.warnings .count { color: var(--warning); }
- &.failed .count { color: var(--error); }
- &.skipped .count { color: var(--text-tertiary); }
- &.total .count { color: var(--text-primary); }
+ &.passed .count { color: var(--color-status-success); }
+ &.info .count { color: var(--color-status-info); }
+ &.warnings .count { color: var(--color-status-warning); }
+ &.failed .count { color: var(--color-status-error); }
+ &.skipped .count { color: var(--color-text-muted); }
+ &.total .count { color: var(--color-text-primary); }
&.duration .count {
- color: var(--text-secondary);
+ color: var(--color-text-secondary);
font-family: monospace;
font-size: 1.25rem;
}
@@ -99,7 +99,7 @@ import { DoctorSeverity, DoctorSummary } from '../../models/doctor.models';
.summary-divider {
width: 1px;
height: 40px;
- background: var(--border);
+ background: var(--color-border-primary);
}
@media (max-width: 640px) {
diff --git a/src/Web/StellaOps.Web/src/app/features/doctor/doctor-dashboard.component.html b/src/Web/StellaOps.Web/src/app/features/doctor/doctor-dashboard.component.html
index f1e9b61e4..64fb3a1da 100644
--- a/src/Web/StellaOps.Web/src/app/features/doctor/doctor-dashboard.component.html
+++ b/src/Web/StellaOps.Web/src/app/features/doctor/doctor-dashboard.component.html
@@ -9,28 +9,28 @@
class="btn btn-primary"
(click)="runQuickCheck()"
[disabled]="store.isRunning()">
-
⚡
+
Quick Check
- ⚙
+
Normal Check
- 🔍
+
Full Check
- 💾
+
Export
@@ -61,7 +61,7 @@
@if (store.error()) {
-
⚠
+
{{ store.error() }}
Dismiss
@@ -173,7 +173,7 @@
@if (store.state() === 'idle' && !store.hasReport()) {
-
🔍
+
No Diagnostics Run Yet
Click "Quick Check" to run a fast diagnostic, or "Full Check" for comprehensive analysis.
diff --git a/src/Web/StellaOps.Web/src/app/features/doctor/models/registry.models.ts b/src/Web/StellaOps.Web/src/app/features/doctor/models/registry.models.ts
index c73f1c301..d784f0326 100644
--- a/src/Web/StellaOps.Web/src/app/features/doctor/models/registry.models.ts
+++ b/src/Web/StellaOps.Web/src/app/features/doctor/models/registry.models.ts
@@ -118,6 +118,8 @@ export function severityToHealthStatus(severity: DoctorSeverity): RegistryHealth
/**
* Maps capability status to display properties.
*/
+const REGISTRY_SVG_ATTRS = 'xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"';
+
export function getCapabilityStatusDisplay(status: CapabilityStatus): {
icon: string;
label: string;
@@ -125,13 +127,13 @@ export function getCapabilityStatusDisplay(status: CapabilityStatus): {
} {
switch (status) {
case 'supported':
- return { icon: '✔', label: 'Supported', cssClass: 'status-supported' };
+ return { icon: `
`, label: 'Supported', cssClass: 'status-supported' };
case 'unsupported':
- return { icon: '✘', label: 'Not Supported', cssClass: 'status-unsupported' };
+ return { icon: `
`, label: 'Not Supported', cssClass: 'status-unsupported' };
case 'partial':
- return { icon: '⚪', label: 'Partial', cssClass: 'status-partial' };
+ return { icon: `
`, label: 'Partial', cssClass: 'status-partial' };
default:
- return { icon: '?', label: 'Unknown', cssClass: 'status-unknown' };
+ return { icon: `
`, label: 'Unknown', cssClass: 'status-unknown' };
}
}
@@ -145,13 +147,13 @@ export function getHealthStatusDisplay(status: RegistryHealthStatus): {
} {
switch (status) {
case 'healthy':
- return { icon: '✔', label: 'Healthy', cssClass: 'health-healthy' };
+ return { icon: `
`, label: 'Healthy', cssClass: 'health-healthy' };
case 'degraded':
- return { icon: '⚠', label: 'Degraded', cssClass: 'health-degraded' };
+ return { icon: `
`, label: 'Degraded', cssClass: 'health-degraded' };
case 'unhealthy':
- return { icon: '✘', label: 'Unhealthy', cssClass: 'health-unhealthy' };
+ return { icon: `
`, label: 'Unhealthy', cssClass: 'health-unhealthy' };
default:
- return { icon: '?', label: 'Unknown', cssClass: 'health-unknown' };
+ return { icon: `
`, label: 'Unknown', cssClass: 'health-unknown' };
}
}
diff --git a/src/Web/StellaOps.Web/src/app/features/environments/environment-detail-page.component.ts b/src/Web/StellaOps.Web/src/app/features/environments/environment-detail-page.component.ts
index 99b080895..d2eb76270 100644
--- a/src/Web/StellaOps.Web/src/app/features/environments/environment-detail-page.component.ts
+++ b/src/Web/StellaOps.Web/src/app/features/environments/environment-detail-page.component.ts
@@ -28,7 +28,7 @@ interface GateSummary {
template: `