export interface ThemeColors extends Record { "--background": string; "--foreground": string; "--card": string; "--card-foreground": string; "--popover": string; "--popover-foreground": string; "--primary": string; "--primary-foreground": string; "--secondary": string; "--secondary-foreground": string; "--muted": string; "--muted-foreground": string; "--accent": string; "--accent-foreground": string; "--destructive": string; "--destructive-foreground": string; "--success": string; "--success-foreground": string; "--warning": string; "--warning-foreground": string; "--border": string; "--chart-1": string; "--chart-2": string; "--chart-3": string; "--chart-4": string; "--chart-5": string; } export interface Theme { id: string; name: string; colors: ThemeColors; } export const THEMES: Theme[] = [ { id: "donut-mono", name: "Donut Mono", colors: { "--background": "#070707", "--foreground": "#ffffff", "--card": "#0e0e0e", "--card-foreground": "#e4e4e4", "--popover": "#0e0e0e", "--popover-foreground": "#e4e4e4", "--primary": "#ffffff", "--primary-foreground": "#070707", "--secondary": "#161616", "--secondary-foreground": "#e4e4e4", "--muted": "#161616", "--muted-foreground": "#a0a0a0", "--accent": "#1f1f1f", "--accent-foreground": "#ffffff", "--destructive": "#ec6a5e", "--destructive-foreground": "#070707", "--success": "#61c554", "--success-foreground": "#070707", "--warning": "#f4be4f", "--warning-foreground": "#070707", "--border": "rgba(255,255,255,0.06)", "--chart-1": "#a0a0a0", "--chart-2": "#6b6b6b", "--chart-3": "#444444", "--chart-4": "#e4e4e4", "--chart-5": "#ffffff", }, }, { id: "tokyo-night", name: "Tokyo Night", colors: { "--background": "#1a1b26", "--foreground": "#c0caf5", "--card": "#24283b", "--card-foreground": "#c0caf5", "--popover": "#24283b", "--popover-foreground": "#c0caf5", "--primary": "#7aa2f7", "--primary-foreground": "#1a1b26", "--secondary": "#2ac3de", "--secondary-foreground": "#1a1b26", "--muted": "#3b4261", "--muted-foreground": "#a9b1d6", "--accent": "#bb9af7", "--accent-foreground": "#1a1b26", "--destructive": "#f7768e", "--destructive-foreground": "#1a1b26", "--success": "#9ece6a", "--success-foreground": "#1a1b26", "--warning": "#e0af68", "--warning-foreground": "#1a1b26", "--border": "#3b4261", "--chart-1": "#7aa2f7", "--chart-2": "#9ece6a", "--chart-3": "#bb9af7", "--chart-4": "#2ac3de", "--chart-5": "#ff9e64", }, }, { id: "dracula", name: "Dracula", colors: { "--background": "#282a36", "--foreground": "#f8f8f2", "--card": "#44475a", "--card-foreground": "#f8f8f2", "--popover": "#44475a", "--popover-foreground": "#f8f8f2", "--primary": "#bd93f9", "--primary-foreground": "#282a36", "--secondary": "#8be9fd", "--secondary-foreground": "#282a36", "--muted": "#6272a4", "--muted-foreground": "#f8f8f2", "--accent": "#ff79c6", "--accent-foreground": "#282a36", "--destructive": "#ff5555", "--destructive-foreground": "#f8f8f2", "--success": "#50fa7b", "--success-foreground": "#282a36", "--warning": "#ffb86c", "--warning-foreground": "#282a36", "--border": "#6272a4", "--chart-1": "#bd93f9", "--chart-2": "#50fa7b", "--chart-3": "#ff79c6", "--chart-4": "#8be9fd", "--chart-5": "#ffb86c", }, }, { id: "matchalk", name: "Matchalk", colors: { "--background": "#273136", "--foreground": "#D1DED3", "--card": "#1c2427", "--card-foreground": "#D1DED3", "--popover": "#323e45", "--popover-foreground": "#D1DED3", "--primary": "#7eb08a", "--primary-foreground": "#273136", "--secondary": "#d2b48c", "--secondary-foreground": "#273136", "--muted": "#323e45", "--muted-foreground": "#7ea4b0", "--accent": "#d2b48c", "--accent-foreground": "#273136", "--destructive": "#ff819f", "--destructive-foreground": "#273136", "--success": "#a8c97f", "--success-foreground": "#273136", "--warning": "#e6c07b", "--warning-foreground": "#273136", "--border": "#304e37", "--chart-1": "#7eb08a", "--chart-2": "#d2b48c", "--chart-3": "#7ea4b0", "--chart-4": "#a8c97f", "--chart-5": "#e6c07b", }, }, { id: "houston", name: "Houston", colors: { "--background": "#17191e", "--foreground": "#f7f7f8", "--card": "#21252e", "--card-foreground": "#f7f7f8", "--popover": "#21252e", "--popover-foreground": "#f7f7f8", "--primary": "#5755d9", "--primary-foreground": "#f7f7f8", "--secondary": "#f25f4c", "--secondary-foreground": "#f7f7f8", "--muted": "#2a2e39", "--muted-foreground": "#9ca3af", "--accent": "#0ea5e9", "--accent-foreground": "#f7f7f8", "--destructive": "#ef4444", "--destructive-foreground": "#f7f7f8", "--success": "#22c55e", "--success-foreground": "#17191e", "--warning": "#f59e0b", "--warning-foreground": "#17191e", "--border": "#2a2e39", "--chart-1": "#5755d9", "--chart-2": "#0ea5e9", "--chart-3": "#f25f4c", "--chart-4": "#22c55e", "--chart-5": "#f59e0b", }, }, { id: "ayu-dark", name: "Ayu Dark", colors: { "--background": "#0a0e14", "--foreground": "#b3b1ad", "--card": "#11151c", "--card-foreground": "#b3b1ad", "--popover": "#11151c", "--popover-foreground": "#b3b1ad", "--primary": "#39bae6", "--primary-foreground": "#0a0e14", "--secondary": "#ffb454", "--secondary-foreground": "#0a0e14", "--muted": "#1f2430", "--muted-foreground": "#5c6773", "--accent": "#d2a6ff", "--accent-foreground": "#0a0e14", "--destructive": "#f07178", "--destructive-foreground": "#b3b1ad", "--success": "#c2d94c", "--success-foreground": "#0a0e14", "--warning": "#ffb454", "--warning-foreground": "#0a0e14", "--border": "#1f2430", "--chart-1": "#39bae6", "--chart-2": "#c2d94c", "--chart-3": "#d2a6ff", "--chart-4": "#ffb454", "--chart-5": "#f07178", }, }, { id: "ayu-light", name: "Ayu Light", // Source: ayu-theme/ayu-colors light.yaml. Primary uses the iconic // Ayu orange instead of blue — that's the colour the theme is known for. colors: { "--background": "#f8f9fa", "--foreground": "#5c6166", "--card": "#fcfcfc", "--card-foreground": "#5c6166", "--popover": "#ffffff", "--popover-foreground": "#5c6166", "--primary": "#f29718", "--primary-foreground": "#ffffff", "--secondary": "#399ee6", "--secondary-foreground": "#ffffff", "--muted": "#ebeef0", "--muted-foreground": "#828e9f", "--accent": "#a37acc", "--accent-foreground": "#ffffff", "--destructive": "#e65050", "--destructive-foreground": "#ffffff", "--success": "#86b300", "--success-foreground": "#ffffff", "--warning": "#fa8532", "--warning-foreground": "#ffffff", "--border": "#c8d0d6", "--chart-1": "#f29718", "--chart-2": "#86b300", "--chart-3": "#a37acc", "--chart-4": "#399ee6", "--chart-5": "#4cbf99", }, }, { id: "catppuccin-latte", name: "Catppuccin Latte", // Source: github.com/catppuccin/palette/blob/main/palette.json // Primary uses mauve (purple) — the colour Catppuccin is most known // for — instead of blue, to differentiate from the many blue themes. // Frappé and Macchiato variants intentionally omitted; they're tonal // mid-points between Latte and Mocha and added little variety. colors: { "--background": "#eff1f5", "--foreground": "#4c4f69", "--card": "#ccd0da", "--card-foreground": "#4c4f69", "--popover": "#ccd0da", "--popover-foreground": "#4c4f69", "--primary": "#8839ef", "--primary-foreground": "#eff1f5", "--secondary": "#1e66f5", "--secondary-foreground": "#eff1f5", "--muted": "#bcc0cc", "--muted-foreground": "#6c6f85", "--accent": "#ea76cb", "--accent-foreground": "#eff1f5", "--destructive": "#d20f39", "--destructive-foreground": "#eff1f5", "--success": "#40a02b", "--success-foreground": "#eff1f5", "--warning": "#df8e1d", "--warning-foreground": "#eff1f5", "--border": "#9ca0b0", "--chart-1": "#8839ef", "--chart-2": "#40a02b", "--chart-3": "#ea76cb", "--chart-4": "#04a5e5", "--chart-5": "#fe640b", }, }, { id: "catppuccin-mocha", name: "Catppuccin Mocha", // Source: github.com/catppuccin/palette/blob/main/palette.json // Primary uses mauve (purple) — Catppuccin's signature colour. colors: { "--background": "#1e1e2e", "--foreground": "#cdd6f4", "--card": "#313244", "--card-foreground": "#cdd6f4", "--popover": "#313244", "--popover-foreground": "#cdd6f4", "--primary": "#cba6f7", "--primary-foreground": "#1e1e2e", "--secondary": "#89b4fa", "--secondary-foreground": "#1e1e2e", "--muted": "#45475a", "--muted-foreground": "#a6adc8", "--accent": "#f5c2e7", "--accent-foreground": "#1e1e2e", "--destructive": "#f38ba8", "--destructive-foreground": "#1e1e2e", "--success": "#a6e3a1", "--success-foreground": "#1e1e2e", "--warning": "#f9e2af", "--warning-foreground": "#1e1e2e", "--border": "#585b70", "--chart-1": "#cba6f7", "--chart-2": "#a6e3a1", "--chart-3": "#f5c2e7", "--chart-4": "#89dceb", "--chart-5": "#fab387", }, }, { id: "nord", name: "Nord", // Source: nordtheme.com/docs/colors-and-palettes (Polar Night / Snow Storm / Frost / Aurora) colors: { "--background": "#2e3440", "--foreground": "#d8dee9", "--card": "#3b4252", "--card-foreground": "#d8dee9", "--popover": "#3b4252", "--popover-foreground": "#d8dee9", "--primary": "#81a1c1", "--primary-foreground": "#2e3440", "--secondary": "#88c0d0", "--secondary-foreground": "#2e3440", "--muted": "#434c5e", "--muted-foreground": "#d8dee9", "--accent": "#b48ead", "--accent-foreground": "#2e3440", "--destructive": "#bf616a", "--destructive-foreground": "#eceff4", "--success": "#a3be8c", "--success-foreground": "#2e3440", "--warning": "#ebcb8b", "--warning-foreground": "#2e3440", "--border": "#4c566a", "--chart-1": "#81a1c1", "--chart-2": "#a3be8c", "--chart-3": "#b48ead", "--chart-4": "#88c0d0", "--chart-5": "#d08770", }, }, { id: "gruvbox-dark", name: "Gruvbox Dark", // Source: github.com/morhetz/gruvbox medium-contrast dark palette. // Primary uses the iconic Gruvbox orange instead of blue. colors: { "--background": "#282828", "--foreground": "#ebdbb2", "--card": "#3c3836", "--card-foreground": "#ebdbb2", "--popover": "#3c3836", "--popover-foreground": "#ebdbb2", "--primary": "#fe8019", "--primary-foreground": "#282828", "--secondary": "#83a598", "--secondary-foreground": "#282828", "--muted": "#504945", "--muted-foreground": "#a89984", "--accent": "#d3869b", "--accent-foreground": "#282828", "--destructive": "#fb4934", "--destructive-foreground": "#282828", "--success": "#b8bb26", "--success-foreground": "#282828", "--warning": "#fabd2f", "--warning-foreground": "#282828", "--border": "#665c54", "--chart-1": "#fe8019", "--chart-2": "#b8bb26", "--chart-3": "#d3869b", "--chart-4": "#83a598", "--chart-5": "#8ec07c", }, }, { id: "gruvbox-light", name: "Gruvbox Light", // Source: github.com/morhetz/gruvbox medium-contrast light palette. // Primary uses the iconic Gruvbox orange instead of blue. colors: { "--background": "#fbf1c7", "--foreground": "#3c3836", "--card": "#ebdbb2", "--card-foreground": "#3c3836", "--popover": "#ebdbb2", "--popover-foreground": "#3c3836", "--primary": "#af3a03", "--primary-foreground": "#fbf1c7", "--secondary": "#076678", "--secondary-foreground": "#fbf1c7", "--muted": "#d5c4a1", "--muted-foreground": "#7c6f64", "--accent": "#8f3f71", "--accent-foreground": "#fbf1c7", "--destructive": "#9d0006", "--destructive-foreground": "#fbf1c7", "--success": "#79740e", "--success-foreground": "#fbf1c7", "--warning": "#b57614", "--warning-foreground": "#fbf1c7", "--border": "#a89984", "--chart-1": "#af3a03", "--chart-2": "#79740e", "--chart-3": "#8f3f71", "--chart-4": "#076678", "--chart-5": "#427b58", }, }, { id: "solarized-dark", name: "Solarized Dark", // Source: ethanschoonover.com/solarized — base03 / base02 / base01 / base00 / base0 / base1 colors: { "--background": "#002b36", "--foreground": "#839496", "--card": "#073642", "--card-foreground": "#839496", "--popover": "#073642", "--popover-foreground": "#839496", "--primary": "#268bd2", "--primary-foreground": "#002b36", "--secondary": "#2aa198", "--secondary-foreground": "#002b36", "--muted": "#073642", "--muted-foreground": "#93a1a1", "--accent": "#6c71c4", "--accent-foreground": "#fdf6e3", "--destructive": "#dc322f", "--destructive-foreground": "#fdf6e3", "--success": "#859900", "--success-foreground": "#002b36", "--warning": "#b58900", "--warning-foreground": "#002b36", "--border": "#586e75", "--chart-1": "#268bd2", "--chart-2": "#859900", "--chart-3": "#6c71c4", "--chart-4": "#2aa198", "--chart-5": "#cb4b16", }, }, { id: "solarized-light", name: "Solarized Light", // Source: ethanschoonover.com/solarized — same accents, inverted base scale colors: { "--background": "#fdf6e3", "--foreground": "#657b83", "--card": "#eee8d5", "--card-foreground": "#657b83", "--popover": "#eee8d5", "--popover-foreground": "#657b83", "--primary": "#268bd2", "--primary-foreground": "#fdf6e3", "--secondary": "#2aa198", "--secondary-foreground": "#fdf6e3", "--muted": "#eee8d5", "--muted-foreground": "#93a1a1", "--accent": "#6c71c4", "--accent-foreground": "#fdf6e3", "--destructive": "#dc322f", "--destructive-foreground": "#fdf6e3", "--success": "#859900", "--success-foreground": "#fdf6e3", "--warning": "#b58900", "--warning-foreground": "#fdf6e3", "--border": "#cdc7b3", "--chart-1": "#268bd2", "--chart-2": "#859900", "--chart-3": "#6c71c4", "--chart-4": "#2aa198", "--chart-5": "#cb4b16", }, }, { id: "one-dark", name: "One Dark", // Source: github.com/atom/atom one-dark-syntax/styles/colors.less (mono-1, hue-1..6) colors: { "--background": "#282c34", "--foreground": "#abb2bf", "--card": "#21252b", "--card-foreground": "#abb2bf", "--popover": "#21252b", "--popover-foreground": "#abb2bf", "--primary": "#61afef", "--primary-foreground": "#282c34", "--secondary": "#56b6c2", "--secondary-foreground": "#282c34", "--muted": "#3e4451", "--muted-foreground": "#7d8590", "--accent": "#c678dd", "--accent-foreground": "#282c34", "--destructive": "#e06c75", "--destructive-foreground": "#282c34", "--success": "#98c379", "--success-foreground": "#282c34", "--warning": "#e5c07b", "--warning-foreground": "#282c34", "--border": "#3e4451", "--chart-1": "#61afef", "--chart-2": "#98c379", "--chart-3": "#c678dd", "--chart-4": "#56b6c2", "--chart-5": "#d19a66", }, }, { id: "monokai-pro", name: "Monokai Pro", // Source: classic Monokai filter (monokai-pro.nvim palette/classic.lua). // Primary uses Monokai's signature green instead of cyan. colors: { "--background": "#272822", "--foreground": "#fdfff1", "--card": "#1d1e19", "--card-foreground": "#fdfff1", "--popover": "#1d1e19", "--popover-foreground": "#fdfff1", "--primary": "#a6e22e", "--primary-foreground": "#272822", "--secondary": "#66d9ef", "--secondary-foreground": "#272822", "--muted": "#3b3c35", "--muted-foreground": "#919288", "--accent": "#ae81ff", "--accent-foreground": "#272822", "--destructive": "#f92672", "--destructive-foreground": "#fdfff1", "--success": "#a6e22e", "--success-foreground": "#272822", "--warning": "#e6db74", "--warning-foreground": "#272822", "--border": "#57584f", "--chart-1": "#a6e22e", "--chart-2": "#66d9ef", "--chart-3": "#ae81ff", "--chart-4": "#e6db74", "--chart-5": "#fd971f", }, }, { id: "rose-pine", name: "Rosé Pine", // Source: github.com/rose-pine/palette/blob/main/palette.json. // Primary uses iris (purple) — the iconic Rosé Pine accent — and // success uses pine. Destructive stays love (pink), which is correct // for the palette's red role. colors: { "--background": "#191724", "--foreground": "#e0def4", "--card": "#1f1d2e", "--card-foreground": "#e0def4", "--popover": "#1f1d2e", "--popover-foreground": "#e0def4", "--primary": "#c4a7e7", "--primary-foreground": "#191724", "--secondary": "#9ccfd8", "--secondary-foreground": "#191724", "--muted": "#26233a", "--muted-foreground": "#908caa", "--accent": "#ebbcba", "--accent-foreground": "#191724", "--destructive": "#eb6f92", "--destructive-foreground": "#191724", "--success": "#31748f", "--success-foreground": "#e0def4", "--warning": "#f6c177", "--warning-foreground": "#191724", "--border": "#403d52", "--chart-1": "#c4a7e7", "--chart-2": "#9ccfd8", "--chart-3": "#ebbcba", "--chart-4": "#eb6f92", "--chart-5": "#f6c177", }, }, { id: "rose-pine-dawn", name: "Rosé Pine Dawn", // Source: github.com/rose-pine/palette/blob/main/palette.json (dawn variant). // Primary uses iris (purple) for parity with the dark variant. colors: { "--background": "#faf4ed", "--foreground": "#575279", "--card": "#fffaf3", "--card-foreground": "#575279", "--popover": "#fffaf3", "--popover-foreground": "#575279", "--primary": "#907aa9", "--primary-foreground": "#faf4ed", "--secondary": "#56949f", "--secondary-foreground": "#faf4ed", "--muted": "#f2e9e1", "--muted-foreground": "#797593", "--accent": "#d7827e", "--accent-foreground": "#faf4ed", "--destructive": "#b4637a", "--destructive-foreground": "#faf4ed", "--success": "#286983", "--success-foreground": "#faf4ed", "--warning": "#ea9d34", "--warning-foreground": "#faf4ed", "--border": "#cecacd", "--chart-1": "#907aa9", "--chart-2": "#56949f", "--chart-3": "#d7827e", "--chart-4": "#b4637a", "--chart-5": "#ea9d34", }, }, { id: "github-dark", name: "GitHub Dark", // Source: github.com/primer/primitives base color tokens (dark default) colors: { "--background": "#0d1117", "--foreground": "#f0f6fc", "--card": "#151b23", "--card-foreground": "#f0f6fc", "--popover": "#151b23", "--popover-foreground": "#f0f6fc", "--primary": "#1f6feb", "--primary-foreground": "#f0f6fc", "--secondary": "#58a6ff", "--secondary-foreground": "#0d1117", "--muted": "#212830", "--muted-foreground": "#9198a1", "--accent": "#8957e5", "--accent-foreground": "#f0f6fc", "--destructive": "#da3633", "--destructive-foreground": "#f0f6fc", "--success": "#238636", "--success-foreground": "#f0f6fc", "--warning": "#d29922", "--warning-foreground": "#0d1117", "--border": "#3d444d", "--chart-1": "#1f6feb", "--chart-2": "#238636", "--chart-3": "#8957e5", "--chart-4": "#58a6ff", "--chart-5": "#db6d28", }, }, { id: "github-light", name: "GitHub Light", // Source: github.com/primer/primitives base color tokens (light default) colors: { "--background": "#ffffff", "--foreground": "#25292e", "--card": "#f6f8fa", "--card-foreground": "#25292e", "--popover": "#f6f8fa", "--popover-foreground": "#25292e", "--primary": "#0969da", "--primary-foreground": "#ffffff", "--secondary": "#54aeff", "--secondary-foreground": "#ffffff", "--muted": "#eff2f5", "--muted-foreground": "#59636e", "--accent": "#8250df", "--accent-foreground": "#ffffff", "--destructive": "#cf222e", "--destructive-foreground": "#ffffff", "--success": "#1a7f37", "--success-foreground": "#ffffff", "--warning": "#bf8700", "--warning-foreground": "#ffffff", "--border": "#d1d9e0", "--chart-1": "#0969da", "--chart-2": "#1a7f37", "--chart-3": "#8250df", "--chart-4": "#54aeff", "--chart-5": "#bc4c00", }, }, ]; export const THEME_VARIABLES: Array<{ key: keyof ThemeColors; label: string }> = [ { key: "--background", label: "Background" }, { key: "--foreground", label: "Foreground" }, { key: "--card", label: "Card" }, { key: "--card-foreground", label: "Card FG" }, { key: "--popover", label: "Popover" }, { key: "--popover-foreground", label: "Popover FG" }, { key: "--primary", label: "Primary" }, { key: "--primary-foreground", label: "Primary FG" }, { key: "--secondary", label: "Secondary" }, { key: "--secondary-foreground", label: "Secondary FG" }, { key: "--muted", label: "Muted" }, { key: "--muted-foreground", label: "Muted FG" }, { key: "--accent", label: "Accent" }, { key: "--accent-foreground", label: "Accent FG" }, { key: "--destructive", label: "Destructive" }, { key: "--destructive-foreground", label: "Destructive FG" }, { key: "--success", label: "Success" }, { key: "--success-foreground", label: "Success FG" }, { key: "--warning", label: "Warning" }, { key: "--warning-foreground", label: "Warning FG" }, { key: "--border", label: "Border" }, { key: "--chart-1", label: "Chart 1" }, { key: "--chart-2", label: "Chart 2" }, { key: "--chart-3", label: "Chart 3" }, { key: "--chart-4", label: "Chart 4" }, { key: "--chart-5", label: "Chart 5" }, ]; export function getThemeById(id: string): Theme | undefined { return THEMES.find((theme) => theme.id === id); } export function getThemeByColors( colors: Record, ): Theme | undefined { return THEMES.find((theme) => { return THEME_VARIABLES.every(({ key }) => { return theme.colors[key] === colors[key]; }); }); } export function applyThemeColors(colors: Record): void { const root = document.documentElement; Object.entries(colors).forEach(([key, value]) => { root.style.setProperty(key, value, "important"); }); } export function clearThemeColors(): void { const root = document.documentElement; THEME_VARIABLES.forEach(({ key }) => { root.style.removeProperty(key as string); }); }