-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Problem
monitor-forge's visual customization is currently limited to branding.primaryColor only:
- Dark mode only:
src/styles/base.csshas hard-coded dark theme variables in:rootwith no light mode --accentnot synced with config:base.csshard-codes--accent: #0052CC, not synchronized withbranding.primaryColor- No theme CLI command: There's no
forge themecommand for changing colors/fonts/layout - Fixed panel layout: Sidebar width (
--panel-width: 380px) and position are hard-coded
As a result, every monitor-forge dashboard looks identical. Visual differentiation — essential to the "your own dashboard" value proposition — is impossible.
Solution
1. Extend Config Schema (ThemeSchema)
Extend the existing BrandingSchema with comprehensive theme settings:
export const ThemeSchema = z.object({
mode: z.enum(['dark', 'light', 'auto']).default('dark'),
palette: z.enum([
'default', 'ocean', 'forest', 'sunset', 'midnight',
'cyberpunk', 'minimal', 'custom'
]).default('default'),
colors: z.object({
primary: z.string().regex(/^#[0-9A-Fa-f]{6}$/).default('#0052CC'),
background: z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional(),
foreground: z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional(),
accent: z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional(),
panel: z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional(),
border: z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional(),
}).default({}),
font: z.enum(['system', 'mono', 'inter', 'roboto']).default('system'),
panelPosition: z.enum(['right', 'left', 'bottom']).default('right'),
panelWidth: z.number().min(280).max(600).default(380),
compactMode: z.boolean().default(false),
}).default({});Backward compatible: existing branding.primaryColor maps to theme.colors.primary.
2. Palette Preset System
const PALETTES = {
default: { bg: '#0a0a0f', fg: '#e0e0e0', panel: '#1a1a2e', border: '#2a2a3e', accent: '#0052CC' },
ocean: { bg: '#0a192f', fg: '#ccd6f6', panel: '#112240', border: '#233554', accent: '#64ffda' },
forest: { bg: '#0d1117', fg: '#c9d1d9', panel: '#161b22', border: '#30363d', accent: '#3fb950' },
sunset: { bg: '#1a1020', fg: '#f0e6d3', panel: '#251830', border: '#3d2a4a', accent: '#ff6b6b' },
midnight: { bg: '#020817', fg: '#f8fafc', panel: '#0f172a', border: '#1e293b', accent: '#818cf8' },
cyberpunk: { bg: '#0a0a0a', fg: '#00ff41', panel: '#111111', border: '#1a1a1a', accent: '#ff00ff' },
minimal: { bg: '#ffffff', fg: '#1a1a1a', panel: '#f5f5f5', border: '#e0e0e0', accent: '#000000' },
};3. forge theme CLI Command
# Apply a palette preset
forge theme set --palette ocean
# Set individual colors
forge theme set --primary "#64ffda" --mode dark
# Change layout
forge theme set --panel-position left --panel-width 320 --compact
# View current theme
forge theme status
# Switch dark/light/auto mode
forge theme set --mode light
forge theme set --mode auto # follows OS preference4. Dynamic CSS Variable Injection
In App.ts initialize(), inject config theme values into :root CSS variables:
const theme = config.theme ?? {};
document.documentElement.style.setProperty('--accent', theme.colors?.primary ?? '#0052CC');
document.documentElement.setAttribute('data-theme', theme.mode ?? 'dark');5. Light Mode CSS
Add [data-theme="light"] selector to base.css:
[data-theme="light"] {
--fg: #1a1a1a;
--bg: #ffffff;
--bg-secondary: #f8f9fa;
--bg-panel: #ffffff;
--border: #e0e0e0;
--text-muted: #666;
}
@media (prefers-color-scheme: light) {
[data-theme="auto"] { /* light values */ }
}Implementation Order
forge/src/config/schema.ts— AddThemeSchemaforge/src/theme/palettes.ts— Define palette presets (new file)forge/src/commands/theme.ts—forge theme set/statuscommands (new file)forge/bin/forge.ts— RegisterregisterThemeCommands(program)src/styles/base.css— Add light mode,data-themeattribute-based variable switchingsrc/App.ts— Dynamic theme variable injection logicforge/src/generators/manifest-generator.ts— Include theme info inconfig-resolved.ts
Acceptance Criteria
-
MonitorForgeConfigSchemahas an optionalthemefield, backward compatible with existingbranding.primaryColor -
forge theme set --palette <name>applies one of 7 palette presets -
forge theme set --mode light/dark/autoswitches theme mode -
forge theme set --panel-position left/right/bottomchanges panel position -
forge theme statusdisplays current theme settings -
forge dev/forge buildapplies theme settings as CSS variables to the dashboard -
--mode autoautomatically switches based on OSprefers-color-scheme - All
forge themecommands support--format json,--non-interactive,--dry-runflags - Existing config files without
themefield continue to work (defaults applied) - Tests added:
forge/src/commands/__tests__/theme.test.ts
Priority Rationale
Visual differentiation is essential for the "your own dashboard" value proposition. Forking worldmonitor still requires manual CSS editing to change appearance, but with monitor-forge, forge theme set --palette cyberpunk should give a completely different look in one command. The CSS variable structure is already well-prepared in base.css, making implementation relatively straightforward. If Issue #28 (onboarding) and Issue #29 (presets) attract users, Issue #30 (theming) gives each dashboard its identity and sense of ownership.