From fe0edd7fd26777b337b9b8c7d0b40b55bfe96b27 Mon Sep 17 00:00:00 2001 From: Nettika Date: Thu, 29 Jan 2026 00:34:34 -0800 Subject: [PATCH] Setup a CSS theme system --- TODO.md | 10 +- src/components/AppLayout.vue | 35 +++--- src/style.css | 237 ++++++++++++++++++++++++++--------- 3 files changed, 197 insertions(+), 85 deletions(-) diff --git a/TODO.md b/TODO.md index a01c5747f..38c31eddd 100644 --- a/TODO.md +++ b/TODO.md @@ -69,11 +69,11 @@ A step-by-step checklist for porting MatterControl's design features to a Vue + - [x] Add bottom status bar ### Theme System -- [ ] Define CSS custom properties for colors -- [ ] Define CSS custom properties for spacing -- [ ] Define CSS custom properties for typography -- [ ] Create dark theme color palette -- [ ] Apply theme to all components +- [x] Define CSS custom properties for colors +- [x] Define CSS custom properties for spacing +- [x] Define CSS custom properties for typography +- [x] Create dark theme color palette +- [x] Apply theme to all components ### Sidebar Behavior - [ ] Create collapsible sidebar component diff --git a/src/components/AppLayout.vue b/src/components/AppLayout.vue index 0d9170b81..50faaeb32 100644 --- a/src/components/AppLayout.vue +++ b/src/components/AppLayout.vue @@ -34,8 +34,8 @@ 'header header header' 'left-sidebar viewport right-sidebar' 'status-bar status-bar status-bar'; - grid-template-columns: 240px 1fr 240px; - grid-template-rows: 48px 1fr 24px; + grid-template-columns: var(--sidebar-width) 1fr var(--sidebar-width); + grid-template-rows: var(--header-height) 1fr var(--status-bar-height); height: 100vh; width: 100vw; overflow: hidden; @@ -45,22 +45,21 @@ grid-area: header; display: flex; align-items: center; - padding: 0 16px; - background: #1a1a2e; - border-bottom: 1px solid #2a2a3e; + padding: 0 var(--space-md); + background: var(--color-bg-primary); + border-bottom: 1px solid var(--color-border); } .header h1 { - margin: 0; - font-size: 18px; - font-weight: 500; - color: #e0e0e0; + font-size: var(--font-size-xl); + font-weight: var(--font-weight-medium); + color: var(--color-text-primary); } .left-sidebar { grid-area: left-sidebar; - background: #1e1e2e; - border-right: 1px solid #2a2a3e; + background: var(--color-bg-secondary); + border-right: 1px solid var(--color-border); overflow-y: auto; } @@ -71,8 +70,8 @@ .right-sidebar { grid-area: right-sidebar; - background: #1e1e2e; - border-left: 1px solid #2a2a3e; + background: var(--color-bg-secondary); + border-left: 1px solid var(--color-border); overflow-y: auto; } @@ -80,10 +79,10 @@ grid-area: status-bar; display: flex; align-items: center; - padding: 0 16px; - background: #1a1a2e; - border-top: 1px solid #2a2a3e; - font-size: 12px; - color: #888; + padding: 0 var(--space-md); + background: var(--color-bg-primary); + border-top: 1px solid var(--color-border); + font-size: var(--font-size-sm); + color: var(--color-text-muted); } diff --git a/src/style.css b/src/style.css index 28e960fc6..273e85fe6 100644 --- a/src/style.css +++ b/src/style.css @@ -1,74 +1,187 @@ :root { - font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; - line-height: 1.5; - font-weight: 400; + /* Colors - Dark Theme */ + --color-bg-primary: #1a1a2e; + --color-bg-secondary: #1e1e2e; + --color-bg-tertiary: #252538; + --color-bg-viewport: #1a1a2e; - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; + --color-border: #2a2a3e; + --color-border-light: #3a3a4e; - font-synthesis: none; - text-rendering: optimizeLegibility; + --color-text-primary: #e0e0e0; + --color-text-secondary: #a0a0a0; + --color-text-muted: #666; + + --color-accent: #4a90d9; + --color-accent-hover: #5aa0e9; + --color-accent-active: #3a80c9; + + --color-success: #4caf50; + --color-warning: #ff9800; + --color-error: #f44336; + + /* Spacing */ + --space-xs: 4px; + --space-sm: 8px; + --space-md: 16px; + --space-lg: 24px; + --space-xl: 32px; + + /* Typography */ + --font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, + sans-serif; + --font-family-mono: 'SF Mono', 'Fira Code', 'Consolas', monospace; + + --font-size-xs: 11px; + --font-size-sm: 12px; + --font-size-md: 14px; + --font-size-lg: 16px; + --font-size-xl: 18px; + --font-size-2xl: 24px; + + --font-weight-normal: 400; + --font-weight-medium: 500; + --font-weight-bold: 600; + + --line-height-tight: 1.2; + --line-height-normal: 1.5; + + /* Layout */ + --header-height: 48px; + --status-bar-height: 24px; + --sidebar-width: 240px; + + /* Borders */ + --radius-sm: 4px; + --radius-md: 6px; + --radius-lg: 8px; + + /* Transitions */ + --transition-fast: 0.15s ease; + --transition-normal: 0.25s ease; +} + +/* Base styles */ +*, +*::before, +*::after { + box-sizing: border-box; +} + +html, +body { + margin: 0; + padding: 0; + overflow: hidden; + background-color: var(--color-bg-primary); + color: var(--color-text-primary); + font-family: var(--font-family); + font-size: var(--font-size-md); + line-height: var(--line-height-normal); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } -a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; -} -a:hover { - color: #535bf2; -} - -body { - margin: 0; - overflow: hidden; -} - -h1 { - font-size: 3.2em; - line-height: 1.1; -} - -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; -} -button:hover { - border-color: #646cff; -} -button:focus, -button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; -} - -.card { - padding: 2em; -} - #app { - width: 100%; + width: 100vw; height: 100vh; } -@media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - } - a:hover { - color: #747bff; - } - button { - background-color: #f9f9f9; - } +/* Typography */ +h1, +h2, +h3, +h4, +h5, +h6 { + margin: 0; + font-weight: var(--font-weight-medium); + line-height: var(--line-height-tight); +} + +h1 { + font-size: var(--font-size-xl); +} +h2 { + font-size: var(--font-size-lg); +} +h3 { + font-size: var(--font-size-md); +} + +/* Links */ +a { + color: var(--color-accent); + text-decoration: none; +} + +a:hover { + color: var(--color-accent-hover); +} + +/* Buttons */ +button { + font-family: inherit; + font-size: var(--font-size-sm); + font-weight: var(--font-weight-medium); + padding: var(--space-sm) var(--space-md); + border: 1px solid var(--color-border); + border-radius: var(--radius-md); + background-color: var(--color-bg-tertiary); + color: var(--color-text-primary); + cursor: pointer; + transition: all var(--transition-fast); +} + +button:hover { + background-color: var(--color-bg-secondary); + border-color: var(--color-border-light); +} + +button:active { + background-color: var(--color-bg-primary); +} + +button:focus-visible { + outline: 2px solid var(--color-accent); + outline-offset: 2px; +} + +/* Form inputs */ +input, +select, +textarea { + font-family: inherit; + font-size: var(--font-size-sm); + padding: var(--space-sm); + border: 1px solid var(--color-border); + border-radius: var(--radius-sm); + background-color: var(--color-bg-tertiary); + color: var(--color-text-primary); +} + +input:focus, +select:focus, +textarea:focus { + outline: none; + border-color: var(--color-accent); +} + +/* Scrollbar styling */ +::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +::-webkit-scrollbar-track { + background: var(--color-bg-primary); +} + +::-webkit-scrollbar-thumb { + background: var(--color-border-light); + border-radius: 4px; +} + +::-webkit-scrollbar-thumb:hover { + background: var(--color-text-muted); }