feat(FN-1343): add rust, copper, foundry, carbon color themes
- Add rust, copper, foundry, carbon theme IDs to core types - Register themes in ThemeSelector component and flash-prevention logic - Add CSS theme blocks and swatches for all four new themes - Add ThemeSelector unit tests for the new themes - Update memory documentation with new theme count
This commit is contained in:
@@ -35,8 +35,8 @@
|
||||
|
||||
## Color Theme System
|
||||
|
||||
- There are **34 unique color themes** in `packages/dashboard/app/styles.css` (ocean, forest, sunset, berry, monochrome, slate, ash, graphite, silver, zen, high-contrast, industrial, solarized, factory, ayu, one-dark, nord, dracula, gruvbox, tokyo-night, catppuccin-mocha, github-dark, everforest, rose-pine, kanagawa, night-owl, palenight, monokai-pro, slime, brutalist, neon-city, parchment, terminal, glass). Each has a dark variant `[data-color-theme="<name>"]` and a light variant `[data-color-theme="<name>"][data-theme="light"]`.
|
||||
- When adding CSS custom properties that should be theme-aware (like `--accent`, `--status-*-bg`), add them to all 34 theme blocks plus `:root` and `[data-theme="light"]` base blocks. The test in `status-colors-theme.test.ts` iterates all blocks programmatically to prevent regressions.
|
||||
- There are **48 unique color themes** in `packages/dashboard/app/styles.css` (default, ocean, forest, sunset, zen, berry, high-contrast, industrial, monochrome, slate, ash, graphite, silver, solarized, factory, ayu, one-dark, nord, dracula, gruvbox, tokyo-night, catppuccin-mocha, github-dark, everforest, rose-pine, kanagawa, night-owl, palenight, monokai-pro, slime, brutalist, neon-city, parchment, terminal, glass, horizon, vitesse, outrun, snazzy, porple, espresso, mars, poimandres, ember, rust, copper, foundry, carbon). Each has a dark variant `[data-color-theme="<name>"]` and a light variant `[data-color-theme="<name>"][data-theme="light"]`.
|
||||
- When adding CSS custom properties that should be theme-aware (like `--accent`, `--status-*-bg`), add them to all 48 theme blocks plus `:root` and `[data-theme="light"]` base blocks. The test in `status-colors-theme.test.ts` iterates all blocks programmatically to prevent regressions.
|
||||
|
||||
## Plugin System (FN-1111 / FN-1400)
|
||||
|
||||
|
||||
@@ -55,6 +55,10 @@ export const COLOR_THEMES = [
|
||||
"mars",
|
||||
"poimandres",
|
||||
"ember",
|
||||
"rust",
|
||||
"copper",
|
||||
"foundry",
|
||||
"carbon",
|
||||
] as const;
|
||||
export type ColorTheme = (typeof COLOR_THEMES)[number];
|
||||
|
||||
|
||||
@@ -60,6 +60,10 @@ const COLOR_THEMES: { value: ColorTheme; label: string; className: string }[] =
|
||||
{ value: "mars", label: "Mars", className: "theme-swatch-mars" },
|
||||
{ value: "poimandres", label: "Poimandres", className: "theme-swatch-poimandres" },
|
||||
{ value: "ember", label: "Ember", className: "theme-swatch-ember" },
|
||||
{ value: "rust", label: "Rust", className: "theme-swatch-rust" },
|
||||
{ value: "copper", label: "Copper", className: "theme-swatch-copper" },
|
||||
{ value: "foundry", label: "Foundry", className: "theme-swatch-foundry" },
|
||||
{ value: "carbon", label: "Carbon", className: "theme-swatch-carbon" },
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -116,6 +116,10 @@ describe("ThemeSelector", () => {
|
||||
expect(screen.getByLabelText("Mars theme")).toBeDefined();
|
||||
expect(screen.getByLabelText("Poimandres theme")).toBeDefined();
|
||||
expect(screen.getByLabelText("Ember theme")).toBeDefined();
|
||||
expect(screen.getByLabelText("Rust theme")).toBeDefined();
|
||||
expect(screen.getByLabelText("Copper theme")).toBeDefined();
|
||||
expect(screen.getByLabelText("Foundry theme")).toBeDefined();
|
||||
expect(screen.getByLabelText("Carbon theme")).toBeDefined();
|
||||
});
|
||||
|
||||
it("marks current color theme as active", () => {
|
||||
@@ -438,6 +442,10 @@ describe("ThemeSelector", () => {
|
||||
["espresso", "Espresso"],
|
||||
["mars", "Mars"],
|
||||
["poimandres", "Poimandres"],
|
||||
["rust", "Rust"],
|
||||
["copper", "Copper"],
|
||||
["foundry", "Foundry"],
|
||||
["carbon", "Carbon"],
|
||||
] as const;
|
||||
|
||||
dramaticThemes.forEach(([value, label]) => {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
try {
|
||||
var mode = localStorage.getItem('kb-dashboard-theme-mode') || 'dark';
|
||||
var colorTheme = localStorage.getItem('kb-dashboard-color-theme') || 'default';
|
||||
var validThemes = ['default', 'ocean', 'forest', 'sunset', 'zen', 'berry', 'high-contrast', 'industrial', 'monochrome', 'slate', 'ash', 'graphite', 'silver', 'solarized', 'factory', 'ayu', 'one-dark', 'nord', 'dracula', 'gruvbox', 'tokyo-night', 'catppuccin-mocha', 'github-dark', 'everforest', 'rose-pine', 'kanagawa', 'night-owl', 'palenight', 'monokai-pro', 'slime', 'brutalist', 'neon-city', 'parchment', 'terminal', 'glass', 'horizon', 'vitesse', 'outrun', 'snazzy', 'porple', 'espresso', 'mars', 'poimandres', 'ember'];
|
||||
var validThemes = ['default', 'ocean', 'forest', 'sunset', 'zen', 'berry', 'high-contrast', 'industrial', 'monochrome', 'slate', 'ash', 'graphite', 'silver', 'solarized', 'factory', 'ayu', 'one-dark', 'nord', 'dracula', 'gruvbox', 'tokyo-night', 'catppuccin-mocha', 'github-dark', 'everforest', 'rose-pine', 'kanagawa', 'night-owl', 'palenight', 'monokai-pro', 'slime', 'brutalist', 'neon-city', 'parchment', 'terminal', 'glass', 'horizon', 'vitesse', 'outrun', 'snazzy', 'porple', 'espresso', 'mars', 'poimandres', 'ember', 'rust', 'copper', 'foundry', 'carbon'];
|
||||
if (!validThemes.includes(colorTheme)) {
|
||||
colorTheme = 'default';
|
||||
}
|
||||
|
||||
@@ -12912,6 +12912,250 @@ body[data-color-theme="terminal"][data-theme="light"]::before {
|
||||
--accent: #4090c0;
|
||||
}
|
||||
|
||||
/* RUST - Deep oxidized red-brown with warm rust accents */
|
||||
[data-color-theme="rust"] {
|
||||
--bg: #0f0b09;
|
||||
--surface: #1a1410;
|
||||
--card: #231b14;
|
||||
--card-hover: #2e241c;
|
||||
--border: #4a3528;
|
||||
--text: #f0e8e0;
|
||||
--text-muted: #a09080;
|
||||
--text-dim: #605040;
|
||||
|
||||
--todo: #e06830;
|
||||
--in-progress: #e88040;
|
||||
--in-progress-rgb: 232, 128, 64;
|
||||
--in-review: #40b8b0;
|
||||
--triage: #e05030;
|
||||
--done: #a08870;
|
||||
--color-success: #40b8b0;
|
||||
--color-error: #e04030;
|
||||
|
||||
--cta-bg: #a04020;
|
||||
--cta-border: #c05828;
|
||||
--cta-text: #fff;
|
||||
--cta-bg-hover: #c05828;
|
||||
--cta-border-hover: #d87038;
|
||||
--cta-glow: 0 0 8px rgba(199, 91, 42, 0.3);
|
||||
--logo-accent: var(--todo);
|
||||
--color-info: #58a8d0;
|
||||
--accent: #c75b2a;
|
||||
}
|
||||
|
||||
[data-color-theme="rust"][data-theme="light"] {
|
||||
--bg: #f5efe8;
|
||||
--surface: #ebe3d8;
|
||||
--card: #fdfaf6;
|
||||
--card-hover: #f5efe8;
|
||||
--border: #d8d0c0;
|
||||
--text: #1a1510;
|
||||
--text-muted: #685a48;
|
||||
--text-dim: #8a7a66;
|
||||
|
||||
--todo: #a04020;
|
||||
--in-progress: #b85828;
|
||||
--in-progress-rgb: 184, 88, 40;
|
||||
--in-review: #209090;
|
||||
--triage: #c03820;
|
||||
--done: #887868;
|
||||
--color-success: #209090;
|
||||
--color-error: #c03020;
|
||||
|
||||
--cta-bg: #803818;
|
||||
--cta-border: #a04820;
|
||||
--cta-text: #fff;
|
||||
--cta-bg-hover: #a04820;
|
||||
--cta-border-hover: #b85a28;
|
||||
--cta-glow: 0 0 8px rgba(160, 72, 32, 0.3);
|
||||
--logo-accent: var(--todo);
|
||||
--color-info: #2878a0;
|
||||
--accent: #a04520;
|
||||
}
|
||||
|
||||
/* COPPER - Warm metallic copper tones */
|
||||
[data-color-theme="copper"] {
|
||||
--bg: #0e0c0a;
|
||||
--surface: #181410;
|
||||
--card: #211c16;
|
||||
--card-hover: #2c251c;
|
||||
--border: #4d3d2e;
|
||||
--text: #f0e8e0;
|
||||
--text-muted: #a09080;
|
||||
--text-dim: #605040;
|
||||
|
||||
--todo: #e89545;
|
||||
--in-progress: #f0a858;
|
||||
--in-progress-rgb: 240, 168, 88;
|
||||
--in-review: #40c0a8;
|
||||
--triage: #e06838;
|
||||
--done: #a09078;
|
||||
--color-success: #40c0a8;
|
||||
--color-error: #e05830;
|
||||
|
||||
--cta-bg: #b06830;
|
||||
--cta-border: #c87840;
|
||||
--cta-text: #fff;
|
||||
--cta-bg-hover: #c87840;
|
||||
--cta-border-hover: #d88a50;
|
||||
--cta-glow: 0 0 8px rgba(212, 133, 74, 0.3);
|
||||
--logo-accent: var(--todo);
|
||||
--color-info: #60a8d8;
|
||||
--accent: #d4854a;
|
||||
}
|
||||
|
||||
[data-color-theme="copper"][data-theme="light"] {
|
||||
--bg: #f3ede6;
|
||||
--surface: #e8e0d6;
|
||||
--card: #fdfaf7;
|
||||
--card-hover: #f5efe8;
|
||||
--border: #d8d0c0;
|
||||
--text: #1a1512;
|
||||
--text-muted: #665a50;
|
||||
--text-dim: #8a7a68;
|
||||
|
||||
--todo: #905820;
|
||||
--in-progress: #a87030;
|
||||
--in-progress-rgb: 168, 112, 48;
|
||||
--in-review: #30a090;
|
||||
--triage: #b04820;
|
||||
--done: #887868;
|
||||
--color-success: #30a090;
|
||||
--color-error: #b03020;
|
||||
|
||||
--cta-bg: #785020;
|
||||
--cta-border: #906828;
|
||||
--cta-text: #fff;
|
||||
--cta-bg-hover: #906828;
|
||||
--cta-border-hover: #a87830;
|
||||
--cta-glow: 0 0 8px rgba(144, 84, 32, 0.3);
|
||||
--logo-accent: var(--todo);
|
||||
--color-info: #306090;
|
||||
--accent: #b06830;
|
||||
}
|
||||
|
||||
/* FOUNDRY - Molten metal with orange-yellow glows */
|
||||
[data-color-theme="foundry"] {
|
||||
--bg: #0c0806;
|
||||
--surface: #15100c;
|
||||
--card: #1e1610;
|
||||
--card-hover: #28201a;
|
||||
--border: #40301e;
|
||||
--text: #f0e8e0;
|
||||
--text-muted: #a09080;
|
||||
--text-dim: #605040;
|
||||
|
||||
--todo: #ffab20;
|
||||
--in-progress: #ffc040;
|
||||
--in-progress-rgb: 255, 192, 64;
|
||||
--in-review: #40c0c0;
|
||||
--triage: #ff6020;
|
||||
--done: #a08060;
|
||||
--color-success: #40c0c0;
|
||||
--color-error: #ff4020;
|
||||
|
||||
--cta-bg: #c07808;
|
||||
--cta-border: #e08a10;
|
||||
--cta-text: #1a1008;
|
||||
--cta-bg-hover: #e08a10;
|
||||
--cta-border-hover: #f09a18;
|
||||
--cta-glow: 0 0 12px rgba(240, 154, 24, 0.4);
|
||||
--logo-accent: var(--todo);
|
||||
--color-info: #60b0e0;
|
||||
--accent: #f0960a;
|
||||
}
|
||||
|
||||
[data-color-theme="foundry"][data-theme="light"] {
|
||||
--bg: #f5f0e6;
|
||||
--surface: #ebe4d6;
|
||||
--card: #fdfaf5;
|
||||
--card-hover: #f5f0e8;
|
||||
--border: #d0c4b0;
|
||||
--text: #1a1510;
|
||||
--text-muted: #685a48;
|
||||
--text-dim: #8a7a66;
|
||||
|
||||
--todo: #c07808;
|
||||
--in-progress: #d08a10;
|
||||
--in-progress-rgb: 208, 138, 16;
|
||||
--in-review: #208080;
|
||||
--triage: #a04818;
|
||||
--done: #787068;
|
||||
--color-success: #208080;
|
||||
--color-error: #a03020;
|
||||
|
||||
--cta-bg: #906008;
|
||||
--cta-border: #b07810;
|
||||
--cta-text: #fff;
|
||||
--cta-bg-hover: #b07810;
|
||||
--cta-border-hover: #c08818;
|
||||
--cta-glow: 0 0 8px rgba(192, 120, 16, 0.3);
|
||||
--logo-accent: var(--todo);
|
||||
--color-info: #3070a0;
|
||||
--accent: #c07808;
|
||||
}
|
||||
|
||||
/* CARBON - Graphite-like carbon fiber aesthetic */
|
||||
[data-color-theme="carbon"] {
|
||||
--bg: #0a0b0d;
|
||||
--surface: #111318;
|
||||
--card: #181b22;
|
||||
--card-hover: #222630;
|
||||
--border: #2a3040;
|
||||
--text: #e8ecf0;
|
||||
--text-muted: #8090a0;
|
||||
--text-dim: #505860;
|
||||
|
||||
--todo: #6a9acf;
|
||||
--in-progress: #7ab0e0;
|
||||
--in-progress-rgb: 122, 176, 224;
|
||||
--in-review: #80c0c0;
|
||||
--triage: #c08080;
|
||||
--done: #687080;
|
||||
--color-success: #80c0c0;
|
||||
--color-error: #c06060;
|
||||
|
||||
--cta-bg: #406080;
|
||||
--cta-border: #5a7898;
|
||||
--cta-text: #fff;
|
||||
--cta-bg-hover: #5a7898;
|
||||
--cta-border-hover: #6a8aac;
|
||||
--cta-glow: 0 0 8px rgba(90, 122, 160, 0.3);
|
||||
--logo-accent: var(--todo);
|
||||
--color-info: #6090c0;
|
||||
--accent: #5a8abf;
|
||||
}
|
||||
|
||||
[data-color-theme="carbon"][data-theme="light"] {
|
||||
--bg: #eef0f4;
|
||||
--surface: #e2e6ec;
|
||||
--card: #f5f7fa;
|
||||
--card-hover: #eef2f6;
|
||||
--border: #c0c8d0;
|
||||
--text: #1a2028;
|
||||
--text-muted: #586070;
|
||||
--text-dim: #78808a;
|
||||
|
||||
--todo: #3a6090;
|
||||
--in-progress: #4878a0;
|
||||
--in-progress-rgb: 72, 120, 160;
|
||||
--in-review: #4090a0;
|
||||
--triage: #a05050;
|
||||
--done: #687080;
|
||||
--color-success: #4090a0;
|
||||
--color-error: #a03030;
|
||||
|
||||
--cta-bg: #305878;
|
||||
--cta-border: #4070a0;
|
||||
--cta-text: #fff;
|
||||
--cta-bg-hover: #4070a0;
|
||||
--cta-border-hover: #5080b0;
|
||||
--cta-glow: 0 0 8px rgba(64, 112, 160, 0.3);
|
||||
--logo-accent: var(--todo);
|
||||
--color-info: #306090;
|
||||
--accent: #3a6a9f;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
THEME SELECTOR COMPONENT STYLES
|
||||
============================================================ */
|
||||
@@ -13086,6 +13330,26 @@ body[data-color-theme="terminal"][data-theme="light"]::before {
|
||||
--surface: #27272a;
|
||||
}
|
||||
|
||||
.theme-swatch-rust {
|
||||
--bg: #0f0b09;
|
||||
--surface: #1a1410;
|
||||
}
|
||||
|
||||
.theme-swatch-copper {
|
||||
--bg: #0e0c0a;
|
||||
--surface: #181410;
|
||||
}
|
||||
|
||||
.theme-swatch-foundry {
|
||||
--bg: #0c0806;
|
||||
--surface: #15100c;
|
||||
}
|
||||
|
||||
.theme-swatch-carbon {
|
||||
--bg: #0a0b0d;
|
||||
--surface: #111318;
|
||||
}
|
||||
|
||||
.theme-swatch-silver {
|
||||
--bg: #27272a;
|
||||
--surface: #3f3f46;
|
||||
@@ -13382,6 +13646,26 @@ body[data-color-theme="terminal"][data-theme="light"]::before {
|
||||
--surface: #f4f4f5;
|
||||
}
|
||||
|
||||
[data-theme="light"] .theme-swatch-rust {
|
||||
--bg: #f5efe8;
|
||||
--surface: #ebe3d8;
|
||||
}
|
||||
|
||||
[data-theme="light"] .theme-swatch-copper {
|
||||
--bg: #f3ede6;
|
||||
--surface: #e8e0d6;
|
||||
}
|
||||
|
||||
[data-theme="light"] .theme-swatch-foundry {
|
||||
--bg: #f5f0e6;
|
||||
--surface: #ebe4d6;
|
||||
}
|
||||
|
||||
[data-theme="light"] .theme-swatch-carbon {
|
||||
--bg: #eef0f4;
|
||||
--surface: #e2e6ec;
|
||||
}
|
||||
|
||||
[data-theme="light"] .theme-swatch-silver {
|
||||
--bg: #fafafa;
|
||||
--surface: #f4f4f5;
|
||||
|
||||
@@ -41,6 +41,10 @@
|
||||
"palenight",
|
||||
"monokai-pro",
|
||||
"slime",
|
||||
"rust",
|
||||
"copper",
|
||||
"foundry",
|
||||
"carbon",
|
||||
];
|
||||
|
||||
if (!validThemes.includes(colorTheme)) {
|
||||
|
||||
Reference in New Issue
Block a user