Theming
Vuetty includes a theming system for customizing the appearance of terminal apps. It is flexible enough for global styling and precise enough for per-component control.
Overview
With the theming system, you can:
- Define global color schemes with multiple color formats (named, hex, RGB)
- Customize individual component appearances
- Create consistent UI experiences across your application
- Support light and dark themes
- Easily switch between different themes
- Mix and match color formats based on your needs
How Theming Works
Theme Structure
A Vuetty theme is a JavaScript object that defines your application's visual style. Themes are hierarchical and support multiple color formats:
const theme = {
// Global settings - mix color formats as needed
background: 'black', // Named color
foreground: '#d4d4d4', // Hex color
// Semantic colors - choose format based on use case
primary: '#569cd6', // Hex for brand colors
secondary: 'gray', // Named for standard colors
success: 'rgb(106, 153, 85)', // RGB for programmatic colors
warning: 'yellow',
danger: '#f44747',
info: 'cyan',
// Component-specific settings
components: {
box: {
color: '#569cd6', // Component border color
bg: 'black' // Component background
},
textInput: {
color: 'white',
bg: '#1e1e1e',
focusColor: 'rgb(86, 156, 214)',
errorColor: 'red'
},
button: {
variants: {
primary: { bg: 'blue', color: 'white', bold: true }
}
}
// ... other components
}
};Supported Color Formats
Vuetty supports three color formats through colorUtils.js:
Named Colors:
'red','blue','cyan','magenta','blackBright', etc.- Best for standard terminal colors
Hex Colors:
'#FF5733','#f57'(3 or 6 digits)- Best for precise brand colors
- Supports shorthand notation
RGB Colors:
'rgb(255, 87, 51)'(values 0-255)- Best for programmatic color generation
- Spaces are optional
Theme Application
Apply themes when creating your Vuetty app:
import { vuetty } from 'vuetty';
import MyApp from './MyApp.vue';
const app = vuetty(MyApp, {
theme: myCustomTheme
});Theme Inheritance
Vuetty uses a deep merge strategy:
- Start with the default theme
- Merge in your custom theme
- Component-specific settings override global settings
- More specific settings override less specific ones
This lets you override only what you need and keep the rest of the defaults.
Theme Components
Global Settings
Global settings apply to the entire application:
background: The background color for the entire terminal screenforeground: The default text color for all components
Semantic Colors
Semantic colors provide meaning to your UI elements:
primary: Main accent color for primary actionssecondary: Secondary accent color for less important actionssuccess: Indicates successful operationswarning: Indicates warnings or cautionary statesdanger: Indicates errors or dangerous actionsinfo: Provides informational context
Component-Specific Settings
Each component can define its own theme settings. For example:
components: {
button: {
variants: {
primary: { bg: 'blue', color: 'white', bold: true },
secondary: { bg: 'gray', color: 'white', bold: false }
}
},
textInput: {
color: 'white',
bg: 'black',
focusColor: 'cyan',
errorColor: 'red'
}
}Advanced Theming
Dynamic Themes
You can change themes at runtime:
import { useTheme } from 'vuetty';
const { setTheme } = useTheme();
// Switch to dark theme
setTheme(darkTheme);
// Switch to light theme
setTheme(lightTheme);Theme Extensions
Extend existing themes instead of starting from scratch:
const myTheme = {
...baseTheme,
primary: 'purple',
components: {
...baseTheme.components,
button: {
...baseTheme.components.button,
variants: {
...baseTheme.components.button.variants,
primary: { bg: 'purple', color: 'white' }
}
}
}
};Conditional Theming
Apply themes conditionally based on environment or user preferences:
const theme = isDarkMode
? darkTheme
: lightTheme;
const app = vuetty(MyApp, { theme });Theme Examples
Basic Theme (Named Colors)
const basicTheme = {
background: 'black',
foreground: 'white',
primary: 'blue',
secondary: 'cyan',
success: 'green',
warning: 'yellow',
danger: 'red',
components: {
button: {
variants: {
primary: { bg: 'blue', color: 'white' },
danger: { bg: 'red', color: 'white' }
}
}
}
};Modern Theme (Mixed Formats)
const modernTheme = {
// Global: Hex for precise branding
background: '#1e1e1e',
foreground: '#d4d4d4',
// Semantic: Mix of hex and named
primary: '#569cd6',
secondary: '#4ec9b0',
success: '#6a9955',
warning: 'yellow', // Named for compatibility
danger: '#f44747',
info: '#9cdcfe',
components: {
box: {
color: '#569cd6',
bg: '#1e1e1e'
},
textInput: {
color: '#d4d4d4',
bg: 'black', // Named when precise value not needed
focusColor: 'rgb(86, 156, 214)', // RGB for potential programmatic use
errorColor: '#f44747'
},
button: {
variants: {
primary: {
bg: '#569cd6',
color: '#ffffff',
bold: true
},
secondary: {
bg: '#4ec9b0',
color: '#1e1e1e',
bold: false
},
danger: {
bg: 'rgb(244, 71, 71)', // RGB for dynamic theming
color: 'white', // Named for simplicity
bold: true
}
}
}
}
};Programmatic Theme (RGB Focus)
// Generate a theme programmatically
function createTheme(primaryR, primaryG, primaryB) {
return {
background: '#000',
foreground: '#fff',
primary: `rgb(${primaryR}, ${primaryG}, ${primaryB})`,
// Generate lighter variant
primaryLight: `rgb(${Math.min(primaryR + 50, 255)}, ${Math.min(primaryG + 50, 255)}, ${Math.min(primaryB + 50, 255)})`,
// Generate darker variant
primaryDark: `rgb(${Math.max(primaryR - 50, 0)}, ${Math.max(primaryG - 50, 0)}, ${Math.max(primaryB - 50, 0)})`,
components: {
button: {
variants: {
primary: {
bg: `rgb(${primaryR}, ${primaryG}, ${primaryB})`,
color: 'white'
}
}
}
}
};
}
const blueTheme = createTheme(86, 156, 214);
const greenTheme = createTheme(106, 153, 85);Troubleshooting
Theme Not Applying
If your theme isn't applying correctly:
- Verify theme structure: Ensure your theme object follows the correct format
- Check property names: Component names must match exactly (case-sensitive)
- Validate color formats:
- Named: Must be valid chalk color names (lowercase)
- Hex: Must be
#RGBor#RRGGBBformat - RGB: Must be
rgb(r, g, b)format with values 0-255
- Check passing: Ensure you're passing the theme to the
vuetty()function correctly - Component support: Verify your component implements theme support
Color Not Displaying
If colors aren't showing correctly:
// ❌ Common mistakes
color: 'RGB(255, 0, 0)' // Uppercase not supported
color: '#12345' // Invalid hex length
color: 'rgb(300, 0, 0)' // RGB > 255
color: 'rgba(255,0,0,0.5)' // RGBA not supported
// ✅ Correct formats
color: 'rgb(255, 0, 0)' // Lowercase, valid range
color: '#FF0000' // Valid hex
color: 'red' // Valid named colorTheme Conflicts
If you experience theme conflicts:
- Check merge order: Later definitions override earlier ones
- Component specificity: Component settings override global settings
- Duplicate definitions: Ensure no accidental duplicate theme objects
- Test incrementally: Start with a minimal theme and add gradually
- Debug color resolution: Use
getChalkColorChain()to test color parsing
Performance Issues
If your theme is slow:
- Profile color usage: Check if colors are resolved repeatedly
- Use named colors: Fastest resolution (no regex)
- Avoid dynamic generation: Don't create color strings in render loops
- Reduce unique colors: Better caching with fewer colors
- Check terminal support: Some terminals are slower with true color
Further Reading
Color System Deep Dive
For detailed information about how colors work in Vuetty, including:
- Color format specifications
- Performance optimizations
- Color resolution pipeline
- Technical implementation details
See the Color Management Guide.
Color Utilities API
Vuetty's color system is implemented in src/utils/colorUtils.js, which provides:
getChalkColorChain(color, chalkChain)- Resolve foreground colorsgetChalkBgChain(bg, chalkChain)- Resolve background colorsnormalizeColorForCache(color)- Normalize colors for caching
All three formats (named, hex, RGB) are supported with optimized regex patterns for fast resolution.
