- Cleaned up import statements in options.ts for better organization. - Enhanced the init function to load settings with improved formatting. - Streamlined event listener attachments for buttons and inputs. - Improved the rendering functions for color and semantic grids. - Updated the parsePalette function in themeEngine.ts for better error handling and readability. - Refactored deriveSemanticTheme to enhance clarity and maintainability. - Adjusted tsconfig.json for better structure and readability. - Updated vite.config.ts to improve build process and maintain consistency.
Gogh Theme Engine
A production-grade Chrome Extension (Manifest V3) that applies Gogh-style 16-color terminal palettes to all web pages by intelligently computing and rewriting styles at runtime using perceptual OKLCH color remapping.
Features
- OKLCH Perceptual Color Model — All color math uses OKLCH for perceptually uniform transformations
- Semantic Theme Derivation — Automatically maps 16 terminal colors to semantic roles (error, success, accent, etc.) using hue + brightness analysis
- Smart DOM Processing — TreeWalker-based traversal, MutationObserver with batched/debounced updates, Shadow DOM support
- WCAG Contrast Enforcement — Ensures minimum 4.5:1 contrast ratio for text
- Gradient & SVG Support — Parses and transforms CSS gradients and inline SVG fill/stroke
- Dynamic CSS Class Generation — Reuses CSS classes instead of inline styles for performance
- Per-site Toggle — Enable/disable theming on a per-domain basis
- Intensity Slider — Blend between original and themed colors (0–100%)
- Options Page — YAML editor, live preview, contrast diagnostics, site management
Build Instructions
Prerequisites
- Node.js ≥ 18
- npm ≥ 9
Setup
cd gogh-theme-engine
npm install
Development Build (with watch)
npm run dev
Production Build
npm run build
Load in Chrome
- Run
npm run build - Open
chrome://extensions/ - Enable Developer mode
- Click Load unpacked
- Select the
dist/folder
Project Structure
src/
├── background.ts # Service worker — storage, per-domain toggle, messaging
├── content.ts # Content script — DOM processing, style injection, coordination
├── color.ts # OKLCH color engine — all color math and transforms
├── themeEngine.ts # YAML parsing, validation, semantic theme derivation
├── observer.ts # DOM observation — TreeWalker, MutationObserver, Shadow DOM
├── js-yaml.d.ts # Type declarations for js-yaml
└── options/
├── options.html # Options page UI
└── options.ts # Options page controller
public/
├── manifest.json # Manifest V3 configuration
└── icons/ # Extension icons
Palette Format
Provide a YAML palette in Gogh format:
name: My Theme
author: Your Name
variant: dark
color_01: "#282a36"
color_02: "#ff5555"
color_03: "#50fa7b"
color_04: "#f1fa8c"
color_05: "#bd93f9"
color_06: "#ff79c6"
color_07: "#8be9fd"
color_08: "#f8f8f2"
color_09: "#44475a"
color_10: "#ff6e6e"
color_11: "#69ff94"
color_12: "#ffffa5"
color_13: "#d6acff"
color_14: "#ff92df"
color_15: "#a4ffff"
color_16: "#ffffff"
background: "#282a36"
foreground: "#f8f8f2"
cursor: "#f8f8f2"
How It Works
Color Pipeline
- Parse — CSS color strings are parsed into RGBA
- Convert — RGBA → sRGB → Linear RGB → OKLAB → OKLCH
- Classify — Colors are classified as neutral (achromatic) or chromatic with a hue bucket
- Remap — Neutral colors map to surface/background bands; chromatic colors map to semantic roles
- Contrast — WCAG contrast is enforced by adjusting the L (lightness) channel
- Blend — Final color is blended with original based on intensity setting
- Cache — Results are memoized by color + context key
Theme Derivation
Instead of fixed index mapping, semantic roles are derived by analyzing each palette color's OKLCH properties:
- Red hue range →
error - Green →
success - Blue/Cyan →
accentPrimary/info - Yellow/Orange →
warning - Purple →
accentSecondary - Low chroma →
muted - Surface/border colors are synthesized from background by shifting lightness
Architecture Notes
- No naive CSS inversion or global filters
TreeWalkerfor initial traversal (faster thanquerySelectorAll)MutationObserverwithrequestIdleCallbackbatchingWeakSetfor tracking processed nodesElement.prototype.attachShadowmonkey-patching for shadow DOM- Dynamic CSS class generation with reuse to minimize inline styles
- Transform results cached with LRU-like eviction (max 8192 entries)