Multiple Themes, One System
This is the power move. You have a design system with palette colours, semantic tokens, and scope mappings. Now you’ll create a second theme that reuses the entire system — just with different colours.
The Sunset Theme
Section titled “The Sunset Theme”Create a new file alongside your ocean theme:
sunset.yaml:
config: name: "Sunset" type: dark import: - "./shared/palette.yaml" - "./shared/variables.yaml"
# Override the palettepalette: blue: "#8b4513" cyan: "#ff8c42" gray: "#4a3728" white: "#f5e6d3" red: "#e74c3c" green: "#27ae60" yellow: "#f39c12"
vars: # Override the semantic anchors accent: $$cyan main: $$white std: bg: "#1a1008"
theme: colors: editor.background: $(std.bg) editor.foreground: $(std.fg) editorCursor.foreground: $(std.fg.accent) sideBar.background: $(std.bg.panel) sideBarSectionHeader.background: $(std.bg.panel.inner) activityBar.background: $(std.bg.panel) focusBorder: $(std.outline) widget.shadow: $(std.shadow)
editorError.foreground: $(status.error) editorWarning.foreground: $(status.warning) editorInfo.foreground: $(status.info)
tokenColors: - name: Comments scope: comment settings: foreground: $(scope.comment) - name: Keywords scope: keyword settings: foreground: $(scope.keyword) - name: Strings scope: string settings: foreground: $(scope.string) - name: Numbers scope: constant.numeric settings: foreground: $(scope.number) - name: Functions scope: entity.name.function settings: foreground: $(scope.function) - name: Types scope: entity.name.type settings: foreground: $(scope.type)How Override Cascading Works
Section titled “How Override Cascading Works”The imports load shared/palette.yaml and shared/variables.yaml first, establishing the full design system. Then the main file’s palette and vars sections merge on top. Because objects deep-merge:
palette.bluechanges from#2d5a87to#8b4513palette.cyanchanges from#4a9effto#ff8c42— and sinceaccentis$$cyan, it picks up the new value automaticallystd.bgchanges to#1a1008- Everything else —
std.fg,std.bg.panel,scope.keyword, all of it — recalculates automatically from the new values
You only override what changes. The derived values cascade.
Building Both
Section titled “Building Both”Build both themes in one command:
npx @gesslar/sassy build ocean.yaml sunset.yamlEach gets its own .color-theme.json output. Same structure, completely different feel.
Sharing tokenColors Too
Section titled “Sharing tokenColors Too”If your themes use identical tokenColors, extract them into a shared file:
shared/tokens.yaml:
theme: tokenColors: - name: Comments scope: comment settings: foreground: $(scope.comment) - name: Keywords scope: keyword settings: foreground: $(scope.keyword) - name: Strings scope: string settings: foreground: $(scope.string) - name: Numbers scope: constant.numeric settings: foreground: $(scope.number) - name: Functions scope: entity.name.function settings: foreground: $(scope.function) - name: Types scope: entity.name.type settings: foreground: $(scope.type)Then both themes import it:
config: import: - "./shared/palette.yaml" - "./shared/variables.yaml" - "./shared/tokens.yaml"Remember: tokenColors from imports appear first in the output, and the main file’s tokenColors come last. Since VS Code uses first-match semantics, the imported rules take precedence and the main file’s entries act as fallbacks for scopes not already covered.
Watch Mode with Multiple Themes
Section titled “Watch Mode with Multiple Themes”During development, watch all your themes at once:
npx @gesslar/sassy build --watch ocean.yaml sunset.yamlEdit shared/variables.yaml and both themes rebuild. Edit a palette override in sunset.yaml and only that theme rebuilds. Sassy tracks dependencies and rebuilds only what changed.
Next Steps
Section titled “Next Steps”So far every colour has been a hex code. But Sassy supports far more expressive colour formats — and some of them are genuinely better for theme design.

