CUB-21: Build tactical dark mode theme and design system
All checks were successful
Dev Build / build-test (pull_request) Successful in 2m35s
All checks were successful
Dev Build / build-test (pull_request) Successful in 2m35s
This commit is contained in:
@@ -1,10 +1,18 @@
|
||||
// ============================================================================
|
||||
// OpenClaw Control Center — M3 Tactical Dark Theme
|
||||
// ============================================================================
|
||||
// Material Design 3 theming with custom dark palette per design spec.
|
||||
// Section 5.1: Color Palette, Section 5.2: Typography
|
||||
// Main global stylesheet. Imports the design system token modules and
|
||||
// applies the M3 dark theme. All tokens are defined once in
|
||||
// styles/_tokens.scss — SCSS variables and mixins
|
||||
// styles/_css-properties.scss — CSS custom property output
|
||||
// styles/_utilities.scss — utility mixins for components
|
||||
//
|
||||
// Components should @use these modules rather than hardcoding values.
|
||||
// ============================================================================
|
||||
|
||||
@use 'styles/tokens' as tokens;
|
||||
@use 'styles/css-properties' as css-props;
|
||||
@use 'styles/utilities' as utils;
|
||||
@use '@angular/material' as mat;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -21,11 +29,11 @@ $dark-theme: mat.define-theme((
|
||||
tertiary: mat.$violet-palette,
|
||||
),
|
||||
typography: (
|
||||
brand-family: 'Inter, Roboto, sans-serif',
|
||||
plain-family: 'Inter, Roboto, sans-serif',
|
||||
bold-weight: 600,
|
||||
medium-weight: 500,
|
||||
regular-weight: 400,
|
||||
brand-family: tokens.$font-family-brand,
|
||||
plain-family: tokens.$font-family-body,
|
||||
bold-weight: tokens.$font-weight-bold,
|
||||
medium-weight: tokens.$font-weight-medium,
|
||||
regular-weight: tokens.$font-weight-regular,
|
||||
),
|
||||
density: (
|
||||
scale: 0,
|
||||
@@ -42,68 +50,17 @@ html {
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Custom CSS Custom Properties — Status Colors
|
||||
// Emit Design System CSS Custom Properties
|
||||
// ---------------------------------------------------------------------------
|
||||
// Per spec Section 5.1 "Status Colors (Semantic — outside M3 tonal system)"
|
||||
// These are NOT part of the M3 tonal palette; they are semantic overrides.
|
||||
// ---------------------------------------------------------------------------
|
||||
:root {
|
||||
// --- Tactical Dark Mode color palette (CUB-47) ---
|
||||
--color-surface: #0F172A;
|
||||
--color-surface-light: #1E293B;
|
||||
--color-primary: #38BDF8;
|
||||
--color-secondary: #2DD4BF;
|
||||
--color-accent: #A78BFA;
|
||||
--color-danger: #F87171;
|
||||
--color-text-primary: #FFFFFF;
|
||||
--color-text-secondary: #94A3B8;
|
||||
--color-border: #334155;
|
||||
|
||||
// --- Status colors ---
|
||||
--status-active: #38BDF8;
|
||||
--status-idle: #2DD4BF;
|
||||
--status-thinking: #A78BFA;
|
||||
--status-error: #F87171;
|
||||
--status-offline: #64748B;
|
||||
|
||||
// --- Status background tints (12% opacity) ---
|
||||
--status-active-bg: rgba(56, 189, 248, 0.12);
|
||||
--status-idle-bg: rgba(45, 212, 191, 0.12);
|
||||
--status-thinking-bg: rgba(167, 139, 250, 0.12);
|
||||
--status-error-bg: rgba(248, 113, 113, 0.12);
|
||||
|
||||
// --- Surface overrides (tactical dark palette) ---
|
||||
--cc-background: #0D0F12;
|
||||
--cc-surface: #13161A;
|
||||
--cc-surface-container: #1C2027;
|
||||
--cc-surface-container-high: #252B33;
|
||||
--cc-on-surface: #E2E8F0;
|
||||
--cc-on-surface-variant: #8A9BB0;
|
||||
--cc-outline: #2D3748;
|
||||
|
||||
// --- Mono font stack ---
|
||||
--cc-font-mono: 'Roboto Mono', 'Cascadia Code', 'Fira Code', monospace;
|
||||
|
||||
// --- Layout constants ---
|
||||
--cc-nav-rail-collapsed-width: 72px;
|
||||
--cc-nav-rail-expanded-width: 256px;
|
||||
--cc-header-height: 64px;
|
||||
--cc-bottom-nav-height: 80px;
|
||||
--cc-card-border-radius: 16px;
|
||||
--cc-card-min-width: 320px;
|
||||
--cc-card-gap: 16px;
|
||||
--cc-card-padding: 20px;
|
||||
--cc-section-padding: 24px;
|
||||
--cc-spacing-unit: 8px;
|
||||
}
|
||||
@include css-props.emit-custom-properties;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Global Body Styles
|
||||
// ---------------------------------------------------------------------------
|
||||
body {
|
||||
background-color: var(--color-surface);
|
||||
background-color: var(--cc-surface-darkest);
|
||||
color: var(--cc-on-surface);
|
||||
font-family: 'Inter', 'Roboto', sans-serif;
|
||||
font-family: var(--cc-font-body);
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
min-height: 100vh;
|
||||
@@ -111,37 +68,60 @@ body {
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// M3 Surface Overrides
|
||||
// ---------------------------------------------------------------------------
|
||||
// Override M3 surface tokens to match our tactical dark palette
|
||||
// ---------------------------------------------------------------------------
|
||||
:root {
|
||||
// Override M3 system color tokens to match custom palette
|
||||
--mat-sys-surface: var(--cc-surface);
|
||||
--mat-sys-surface-container: var(--cc-surface-container);
|
||||
--mat-sys-surface-container-high: var(--cc-surface-container-high);
|
||||
--mat-sys-on-surface: var(--cc-on-surface);
|
||||
--mat-sys-on-surface-variant: var(--cc-on-surface-variant);
|
||||
--mat-sys-outline: var(--cc-outline);
|
||||
--mat-sys-background: var(--cc-background);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Typography Helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
.text-mono {
|
||||
font-family: var(--cc-font-mono);
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.02em;
|
||||
font-size: tokens.$font-size-body-medium;
|
||||
font-weight: tokens.$font-weight-regular;
|
||||
letter-spacing: tokens.$letter-spacing-mono;
|
||||
}
|
||||
|
||||
.text-display-large {
|
||||
font-size: tokens.$font-size-display-large;
|
||||
font-weight: tokens.$font-weight-heavy;
|
||||
line-height: tokens.$line-height-tight;
|
||||
letter-spacing: tokens.$letter-spacing-tight;
|
||||
}
|
||||
|
||||
.text-headline-medium {
|
||||
font-size: tokens.$font-size-headline-medium;
|
||||
font-weight: tokens.$font-weight-bold;
|
||||
line-height: tokens.$line-height-tight;
|
||||
}
|
||||
|
||||
.text-title-large {
|
||||
font-size: tokens.$font-size-title-large;
|
||||
font-weight: tokens.$font-weight-bold;
|
||||
}
|
||||
|
||||
.text-title-medium {
|
||||
font-size: tokens.$font-size-title-medium;
|
||||
font-weight: tokens.$font-weight-medium;
|
||||
}
|
||||
|
||||
.text-body-large {
|
||||
font-size: tokens.$font-size-body-large;
|
||||
font-weight: tokens.$font-weight-regular;
|
||||
line-height: tokens.$line-height-normal;
|
||||
}
|
||||
|
||||
.text-body-medium {
|
||||
font-size: tokens.$font-size-body-medium;
|
||||
font-weight: tokens.$font-weight-regular;
|
||||
line-height: tokens.$line-height-normal;
|
||||
}
|
||||
|
||||
.text-label-medium {
|
||||
font-size: tokens.$font-size-label-medium;
|
||||
font-weight: tokens.$font-weight-medium;
|
||||
letter-spacing: tokens.$letter-spacing-wide;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Status Dot Pulse Animations
|
||||
// ---------------------------------------------------------------------------
|
||||
// Per spec Section 7.5: Animation Specs
|
||||
// ---------------------------------------------------------------------------
|
||||
@keyframes pulse-active {
|
||||
0%, 100% { opacity: 1; transform: scale(1); }
|
||||
50% { opacity: 0.7; transform: scale(1.15); }
|
||||
@@ -158,35 +138,35 @@ body {
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Utility Classes
|
||||
// Status Dot Utility Classes
|
||||
// ---------------------------------------------------------------------------
|
||||
.status-dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
width: tokens.$status-dot-size;
|
||||
height: tokens.$status-dot-size;
|
||||
border-radius: tokens.$radius-full;
|
||||
display: inline-block;
|
||||
|
||||
&--active {
|
||||
background-color: var(--status-active);
|
||||
animation: pulse-active 2s ease-in-out infinite;
|
||||
background-color: var(--cc-status-active);
|
||||
animation: pulse-active tokens.$duration-standard tokens.$easing-standard infinite;
|
||||
}
|
||||
|
||||
&--idle {
|
||||
background-color: var(--status-idle);
|
||||
background-color: var(--cc-status-idle);
|
||||
}
|
||||
|
||||
&--thinking {
|
||||
background-color: var(--status-thinking);
|
||||
animation: pulse-thinking 3s ease-in-out infinite;
|
||||
background-color: var(--cc-status-thinking);
|
||||
animation: pulse-thinking 3s tokens.$easing-standard infinite;
|
||||
}
|
||||
|
||||
&--error {
|
||||
background-color: var(--status-error);
|
||||
animation: pulse-error 0.8s ease-in-out infinite;
|
||||
background-color: var(--cc-status-error);
|
||||
animation: pulse-error tokens.$duration-fast tokens.$easing-standard infinite;
|
||||
}
|
||||
|
||||
&--offline {
|
||||
background-color: var(--status-offline);
|
||||
background-color: var(--cc-status-offline);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,6 +181,27 @@ body {
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Screen-reader-only utility
|
||||
// ---------------------------------------------------------------------------
|
||||
.sr-only {
|
||||
@include tokens.sr-only;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Truncate utility
|
||||
// ---------------------------------------------------------------------------
|
||||
.truncate {
|
||||
@include tokens.truncate;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Focus ring utility
|
||||
// ---------------------------------------------------------------------------
|
||||
.focus-ring {
|
||||
@include tokens.focus-ring;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Scrollbar Styling (Tactical Dark)
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -210,11 +211,11 @@ body {
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: var(--cc-surface);
|
||||
background: var(--cc-surface-dark);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--cc-outline);
|
||||
background: var(--cc-surface-lighter);
|
||||
border-radius: 3px;
|
||||
|
||||
&:hover {
|
||||
|
||||
Reference in New Issue
Block a user