fix: improve pill contrast by using WCAG contrast ratios on composited backgrounds

Pills with semi-transparent backgrounds were using raw color luminance to pick
text color, ignoring the page background showing through. This caused unreadable
text on dark themes for mid-luminance colors like orange. Now composites the
rgba background over the actual page bg (dark/light) before computing WCAG
contrast ratios, and centralizes the logic in a shared color-contrast utility.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
dotta 2026-03-23 07:48:50 -05:00
parent e73bc81a73
commit d73c8df895
6 changed files with 118 additions and 27 deletions

View file

@ -1,6 +1,7 @@
import type { CSSProperties } from "react";
import { parseAgentMentionHref, parseProjectMentionHref } from "@paperclipai/shared";
import { getAgentIcon } from "./agent-icons";
import { hexToRgb, pickTextColorForPillBg } from "./color-contrast";
export type ParsedMentionChip =
| {
@ -98,22 +99,10 @@ export function clearMentionChipDecoration(element: HTMLElement) {
function projectMentionColors(color: string): Pick<CSSProperties, "borderColor" | "backgroundColor" | "color"> {
const rgb = hexToRgb(color);
if (!rgb) return {};
const luminance = (0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b) / 255;
return {
borderColor: color,
backgroundColor: `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.22)`,
color: luminance > 0.55 ? "#111827" : "#f8fafc",
};
}
function hexToRgb(hex: string): { r: number; g: number; b: number } | null {
const match = /^#([0-9a-f]{6})$/i.exec(hex.trim());
if (!match) return null;
const value = match[1];
return {
r: parseInt(value.slice(0, 2), 16),
g: parseInt(value.slice(2, 4), 16),
b: parseInt(value.slice(4, 6), 16),
color: pickTextColorForPillBg(color),
};
}