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,5 +1,6 @@
import { useState, useEffect, useRef, useCallback, useMemo, type ChangeEvent, type DragEvent } from "react";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { pickTextColorForSolidBg } from "@/lib/color-contrast";
import { useDialog } from "../context/DialogContext";
import { useCompany } from "../context/CompanyContext";
import { executionWorkspacesApi } from "../api/execution-workspaces";
@ -56,15 +57,6 @@ import { InlineEntitySelector, type InlineEntityOption } from "./InlineEntitySel
const DRAFT_KEY = "paperclip:issue-draft";
const DEBOUNCE_MS = 800;
/** Return black or white hex based on background luminance (WCAG perceptual weights). */
function getContrastTextColor(hexColor: string): string {
const hex = hexColor.replace("#", "");
const r = parseInt(hex.substring(0, 2), 16);
const g = parseInt(hex.substring(2, 4), 16);
const b = parseInt(hex.substring(4, 6), 16);
const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
return luminance > 0.5 ? "#000000" : "#ffffff";
}
interface IssueDraft {
title: string;
@ -915,7 +907,7 @@ export function NewIssueDialog() {
dialogCompany?.brandColor
? {
backgroundColor: dialogCompany.brandColor,
color: getContrastTextColor(dialogCompany.brandColor),
color: pickTextColorForSolidBg(dialogCompany.brandColor),
}
: undefined
}
@ -945,7 +937,7 @@ export function NewIssueDialog() {
c.brandColor
? {
backgroundColor: c.brandColor,
color: getContrastTextColor(c.brandColor),
color: pickTextColorForSolidBg(c.brandColor),
}
: undefined
}