From 084bbcaa91d4f58cd69449065add47d45dcf2ad8 Mon Sep 17 00:00:00 2001
From: Jonas Kunert
Date: Wed, 22 Jul 2026 21:52:50 +0200
Subject: [PATCH] feat: add colorblind-friendly color preset for chat messages
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Message types are distinguished purely by hue (user blue #40a5ff, tool
blue-purple #7c8bed, claude green #2ecc71, tool-result teal #1cc08c,
thinking purple #ba55d3). For color vision deficiency these collapse
into indistinguishable pairs — #41: 'the colour used for You is
indistinguishable to me from the colour used for task or glob or
read' — blue vs purple and green vs teal being classic confusion
pairs.
- new setting claudeCodeChat.ui.colorblindMode (boolean, default off):
contributes entry, settingsData push, updateSettings write-back and
a checkbox in the gear modal (new Appearance group),
- when enabled, body.colorblind-mode re-colors all six message-type
carriers (left border, ::before bar, icon gradients) with a green-
and purple-free palette on the blue-orange and blue-yellow axes
(user #4aa3ff, tool #e58a00, claude #1ec8d4, tool-result #ffd21a,
error #ff4f3b, thinking neutral gray #9aa0a6), luminance-laddered
so the ladder survives even full monochromacy,
- off by default, zero impact while disabled.
Co-Authored-By: Claude Fable 5
---
package.json | 5 +++++
src/extension.ts | 1 +
src/script.ts | 13 ++++++++-----
src/ui-styles.ts | 28 ++++++++++++++++++++++++++++
src/ui.ts | 12 ++++++++++++
5 files changed, 54 insertions(+), 5 deletions(-)
diff --git a/package.json b/package.json
index 6bcdf89..9b6d553 100644
--- a/package.json
+++ b/package.json
@@ -208,6 +208,11 @@
"type": "boolean",
"default": false,
"description": "Enable the local router to convert OpenAI format to Anthropic format. Required for providers that use OpenAI-compatible APIs."
+ },
+ "claudeCodeChat.ui.colorblindMode": {
+ "type": "boolean",
+ "default": false,
+ "description": "Use a colorblind-friendly high-contrast color preset for chat message colors."
}
}
}
diff --git a/src/extension.ts b/src/extension.ts
index 8fa37fb..a4facf2 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -3400,6 +3400,7 @@ class ClaudeChatProvider {
'executable.path': config.get('executable.path', ''),
'environment.variables': config.get>('environment.variables', {}),
'environment.disabled': config.get('environment.disabled', false),
+ 'ui.colorblindMode': config.get('ui.colorblindMode', false),
'isOpenCredits': this._isOpenCredits()
};
diff --git a/src/script.ts b/src/script.ts
index 4c949e2..186b22d 100644
--- a/src/script.ts
+++ b/src/script.ts
@@ -397,10 +397,7 @@ const getScript = (isTelemetryEnabled: boolean, opencreditsApiUrl: string = 'htt
headerDiv.className = 'message-header';
const iconDiv = document.createElement('div');
- iconDiv.className = data.isError ? 'message-icon error' : 'message-icon';
- iconDiv.style.background = data.isError ?
- 'linear-gradient(135deg, #e74c3c 0%, #c0392b 100%)' :
- 'linear-gradient(135deg, #1cc08c 0%, #16a974 100%)';
+ iconDiv.className = data.isError ? 'message-icon result-error' : 'message-icon result-success';
iconDiv.textContent = data.isError ? '❌' : '✅';
const labelDiv = document.createElement('div');
@@ -4863,6 +4860,7 @@ const getScript = (isTelemetryEnabled: boolean, opencreditsApiUrl: string = 'htt
const yoloMode = document.getElementById('yolo-mode').checked;
const executablePath = document.getElementById('executable-path').value;
const useRouter = document.getElementById('use-router')?.checked || false;
+ const colorblindMode = document.getElementById('colorblind-mode').checked;
// Collect environment variables from key-value UI
const envVariables = getEnvVariablesFromUI();
@@ -4902,7 +4900,8 @@ const getScript = (isTelemetryEnabled: boolean, opencreditsApiUrl: string = 'htt
'permissions.yoloMode': yoloMode,
'executable.path': executablePath,
'environment.variables': envVariables,
- 'router.enabled': useRouter
+ 'router.enabled': useRouter,
+ 'ui.colorblindMode': colorblindMode
}
});
}
@@ -5159,6 +5158,10 @@ const getScript = (isTelemetryEnabled: boolean, opencreditsApiUrl: string = 'htt
});
} else if (message.type === 'settingsData') {
// Update UI with current settings
+ document.body.classList.toggle('colorblind-mode', !!message.data['ui.colorblindMode']);
+ document.getElementById('colorblind-mode').checked = !!message.data['ui.colorblindMode'];
+
+
const thinkingIntensity = message.data['thinking.intensity'] || 'think';
const intensityValues = ['think', 'think-hard', 'think-harder', 'ultrathink'];
const sliderValue = intensityValues.indexOf(thinkingIntensity);
diff --git a/src/ui-styles.ts b/src/ui-styles.ts
index 76bd766..17d22a0 100644
--- a/src/ui-styles.ts
+++ b/src/ui-styles.ts
@@ -1171,6 +1171,14 @@ const styles = `
background: linear-gradient(135deg, #e74c3c 0%, #c0392b 100%);
}
+ .message-icon.result-success {
+ background: linear-gradient(135deg, #1cc08c 0%, #16a974 100%);
+ }
+
+ .message-icon.result-error {
+ background: linear-gradient(135deg, #e74c3c 0%, #c0392b 100%);
+ }
+
.message-label {
font-weight: 500;
font-size: 12px;
@@ -1179,6 +1187,26 @@ const styles = `
letter-spacing: 0.5px;
}
+ /* Colorblind preset: green- and purple-free palette, luminance-laddered */
+ body.colorblind-mode .message.user { border-color: rgba(74, 163, 255, 0.55); }
+ body.colorblind-mode .message.user::before { background: linear-gradient(180deg, #4aa3ff 0%, #0f6fd0 100%); }
+ body.colorblind-mode .message.claude { border-color: rgba(30, 200, 212, 0.5); }
+ body.colorblind-mode .message.claude::before { background: linear-gradient(180deg, #1ec8d4 0%, #0f8f99 100%); }
+ body.colorblind-mode .message.error { border-color: rgba(255, 79, 59, 0.55); }
+ body.colorblind-mode .message.error::before { background: linear-gradient(180deg, #ff4f3b 0%, #cc2718 100%); }
+ body.colorblind-mode .message.tool { border-color: rgba(229, 138, 0, 0.55); }
+ body.colorblind-mode .message.tool::before { background: linear-gradient(180deg, #e58a00 0%, #b36b00 100%); }
+ body.colorblind-mode .message.tool-result { border-color: rgba(255, 210, 26, 0.55); }
+ body.colorblind-mode .message.tool-result::before { background: linear-gradient(180deg, #ffd21a 0%, #d9b000 100%); }
+ body.colorblind-mode .message.thinking { border-color: rgba(154, 160, 166, 0.5); }
+ body.colorblind-mode .message.thinking::before { background: linear-gradient(180deg, #9aa0a6 0%, #676c71 100%); }
+ body.colorblind-mode .message-icon.user { background: linear-gradient(135deg, #4aa3ff 0%, #0f6fd0 100%); }
+ body.colorblind-mode .message-icon.claude { background: linear-gradient(135deg, #1ec8d4 0%, #0f8f99 100%); }
+ body.colorblind-mode .message-icon.error { background: linear-gradient(135deg, #ff4f3b 0%, #cc2718 100%); }
+ body.colorblind-mode .message-icon.result-success { background: linear-gradient(135deg, #ffd21a 0%, #d9b000 100%); }
+ body.colorblind-mode .message-icon.result-error { background: linear-gradient(135deg, #ff4f3b 0%, #cc2718 100%); }
+ body.colorblind-mode .tool-icon { background: linear-gradient(135deg, #e58a00 0%, #b36b00 100%); }
+
.message-content {
padding-left: 6px;
}
diff --git a/src/ui.ts b/src/ui.ts
index 4ffcab7..cf1a185 100644
--- a/src/ui.ts
+++ b/src/ui.ts
@@ -414,6 +414,18 @@ const getHtml = (isTelemetryEnabled: boolean, opencreditsApiUrl: string = 'https
+ Appearance
+
+
+
+
+
+
+ High-contrast palette distinguishable with color vision deficiency
+
+
+
+