From badd31ded6674a4669c945be6629c406df65d322 Mon Sep 17 00:00:00 2001 From: arpandhakal Date: Fri, 27 Mar 2026 15:15:04 +0545 Subject: [PATCH 1/2] fix(OUT-3261): use dark text variants for colored multiselect chips Named colors (amber, cyan, teal, etc.) were used directly as chip text color, making them unreadable against the light chip background. Added a color map with dark text-safe variants and a getChipColors utility that returns proper background, border, text, and icon colors. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/components/multiSelect/MultiSelect.tsx | 55 +++++++++-------- src/utils/updateColor.ts | 71 ++++++++++++++++++---- 2 files changed, 88 insertions(+), 38 deletions(-) diff --git a/src/components/multiSelect/MultiSelect.tsx b/src/components/multiSelect/MultiSelect.tsx index f8c8b1c..82a6a45 100644 --- a/src/components/multiSelect/MultiSelect.tsx +++ b/src/components/multiSelect/MultiSelect.tsx @@ -1,7 +1,7 @@ import { Autocomplete, Chip } from '@mui/material'; import { StyledTextInput } from '../styled/StyledTextInput'; import { ClearOutlined, FiberManualRecord } from '@mui/icons-material'; -import { updateColor } from '@/utils/updateColor'; +import { getChipColors } from '@/utils/updateColor'; interface IMultiSelect { data: T[]; @@ -27,31 +27,34 @@ export const MultiSelect = ({ data, nameField, value, getSelec disabled={disabled} renderInput={(params) => } renderTags={(value: T[], getTagProps) => - value.map((option: any, index: number) => ( - } - avatar={} - sx={{ - '&.MuiChip-root': { - borderColor: updateColor(option.color, 0.3), - border: '2px solid', - background: updateColor(option.color, 0.1), - color: option.color, - fontWeight: 500, - }, - '& .MuiChip-deleteIcon': { - color: option.color, - }, - '& .MuiChip-avatar': { - color: option.color, - }, - }} - /> - )) + value.map((option: any, index: number) => { + const chipColors = getChipColors(option.color); + return ( + } + avatar={} + sx={{ + '&.MuiChip-root': { + borderColor: chipColors.border, + border: '2px solid', + background: chipColors.background, + color: chipColors.text, + fontWeight: 500, + }, + '& .MuiChip-deleteIcon': { + color: chipColors.icon, + }, + '& .MuiChip-avatar': { + color: chipColors.icon, + }, + }} + /> + ); + }) } sx={{ '& .MuiOutlinedInput-root': { diff --git a/src/utils/updateColor.ts b/src/utils/updateColor.ts index 211f810..9b9b541 100644 --- a/src/utils/updateColor.ts +++ b/src/utils/updateColor.ts @@ -1,18 +1,65 @@ -export function updateColor(rgbaColor: any, newOpacity: number) { - // Parse the input RGBA color string - const colorRegex = /^rgba\((\d+),\s*(\d+),\s*(\d+),\s*([\d.]+)\)$/; - const match = rgbaColor?.match(colorRegex); +// Map of named colors to their RGB values and a darker text-safe variant +const COLOR_MAP: Record = { + red: { r: 239, g: 68, b: 68, dark: '#991b1b' }, + orange: { r: 249, g: 115, b: 22, dark: '#9a3412' }, + amber: { r: 245, g: 158, b: 11, dark: '#92400e' }, + yellow: { r: 234, g: 179, b: 8, dark: '#854d0e' }, + lime: { r: 132, g: 204, b: 22, dark: '#3f6212' }, + green: { r: 34, g: 197, b: 94, dark: '#166534' }, + emerald: { r: 16, g: 185, b: 129, dark: '#065f46' }, + teal: { r: 20, g: 184, b: 166, dark: '#115e59' }, + cyan: { r: 6, g: 182, b: 212, dark: '#155e75' }, + sky: { r: 14, g: 165, b: 233, dark: '#075985' }, + blue: { r: 59, g: 130, b: 246, dark: '#1e40af' }, + indigo: { r: 99, g: 102, b: 241, dark: '#3730a3' }, + violet: { r: 139, g: 92, b: 246, dark: '#5b21b6' }, + purple: { r: 168, g: 85, b: 247, dark: '#6b21a8' }, + fuchsia: { r: 217, g: 70, b: 239, dark: '#86198f' }, + pink: { r: 236, g: 72, b: 153, dark: '#9d174d' }, + rose: { r: 244, g: 63, b: 94, dark: '#9f1239' }, + gray: { r: 107, g: 114, b: 128, dark: '#374151' }, + slate: { r: 100, g: 116, b: 139, dark: '#334155' }, + zinc: { r: 113, g: 113, b: 122, dark: '#3f3f46' }, + neutral: { r: 115, g: 115, b: 115, dark: '#404040' }, + stone: { r: 120, g: 113, b: 108, dark: '#44403c' }, +}; - if (!rgbaColor || !match) { - // Invalid input format, return the original color - return rgbaColor; +function getColorEntry(color: any): { r: number; g: number; b: number; dark: string } | null { + if (!color || typeof color !== 'string') return null; + + const named = COLOR_MAP[color.toLowerCase()]; + if (named) return named; + + // Fallback: try rgba format + const rgbaMatch = color.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*[\d.]+)?\)$/); + if (rgbaMatch) { + const r = Number(rgbaMatch[1]); + const g = Number(rgbaMatch[2]); + const b = Number(rgbaMatch[3]); + // Darken by 40% for text + const dark = `rgb(${Math.round(r * 0.5)}, ${Math.round(g * 0.5)}, ${Math.round(b * 0.5)})`; + return { r, g, b, dark }; } - // Extract color components - const [, red, green, blue] = match; + return null; +} + +export function updateColor(color: any, newOpacity: number) { + const entry = getColorEntry(color); + if (!entry) return color; + return `rgba(${entry.r}, ${entry.g}, ${entry.b}, ${newOpacity})`; +} - // Build the updated RGBA color string with the new opacity - const updatedColor = `rgba(${red}, ${green}, ${blue}, ${newOpacity})`; +export function getChipColors(color: any): { background: string; border: string; text: string; icon: string } { + const entry = getColorEntry(color); + if (!entry) { + return { background: 'transparent', border: color, text: color, icon: color }; + } - return updatedColor; + return { + background: `rgba(${entry.r}, ${entry.g}, ${entry.b}, 0.1)`, + border: `rgba(${entry.r}, ${entry.g}, ${entry.b}, 0.3)`, + text: entry.dark, + icon: entry.dark, + }; } From 098702ccf01f2a2e21f12972688e9222b0e445fd Mon Sep 17 00:00:00 2001 From: arpandhakal Date: Fri, 27 Mar 2026 15:20:09 +0545 Subject: [PATCH 2/2] fix(OUT-3261): align chip colors with design system primitive tokens Replace arbitrary RGB values with exact hex values from the design system color palette. Each named color now maps to its -100 (background), -200 (border), and darkest available shade (text/icon). Co-Authored-By: Claude Opus 4.6 (1M context) --- src/utils/updateColor.ts | 85 +++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 49 deletions(-) diff --git a/src/utils/updateColor.ts b/src/utils/updateColor.ts index 9b9b541..36324a4 100644 --- a/src/utils/updateColor.ts +++ b/src/utils/updateColor.ts @@ -1,65 +1,52 @@ -// Map of named colors to their RGB values and a darker text-safe variant -const COLOR_MAP: Record = { - red: { r: 239, g: 68, b: 68, dark: '#991b1b' }, - orange: { r: 249, g: 115, b: 22, dark: '#9a3412' }, - amber: { r: 245, g: 158, b: 11, dark: '#92400e' }, - yellow: { r: 234, g: 179, b: 8, dark: '#854d0e' }, - lime: { r: 132, g: 204, b: 22, dark: '#3f6212' }, - green: { r: 34, g: 197, b: 94, dark: '#166534' }, - emerald: { r: 16, g: 185, b: 129, dark: '#065f46' }, - teal: { r: 20, g: 184, b: 166, dark: '#115e59' }, - cyan: { r: 6, g: 182, b: 212, dark: '#155e75' }, - sky: { r: 14, g: 165, b: 233, dark: '#075985' }, - blue: { r: 59, g: 130, b: 246, dark: '#1e40af' }, - indigo: { r: 99, g: 102, b: 241, dark: '#3730a3' }, - violet: { r: 139, g: 92, b: 246, dark: '#5b21b6' }, - purple: { r: 168, g: 85, b: 247, dark: '#6b21a8' }, - fuchsia: { r: 217, g: 70, b: 239, dark: '#86198f' }, - pink: { r: 236, g: 72, b: 153, dark: '#9d174d' }, - rose: { r: 244, g: 63, b: 94, dark: '#9f1239' }, - gray: { r: 107, g: 114, b: 128, dark: '#374151' }, - slate: { r: 100, g: 116, b: 139, dark: '#334155' }, - zinc: { r: 113, g: 113, b: 122, dark: '#3f3f46' }, - neutral: { r: 115, g: 115, b: 115, dark: '#404040' }, - stone: { r: 120, g: 113, b: 108, dark: '#44403c' }, +// Design system primitive tokens mapped to chip color roles +const COLOR_MAP: Record = { + gray: { background: '#F3F4F6', border: '#C9CBCD', dark: '#212B36' }, + blue: { background: '#E6F0FF', border: '#92A9E0', dark: '#053299' }, + green: { background: '#D0FFE8', border: '#115B3B', dark: '#115B3B' }, + red: { background: '#FFEDE8', border: '#991A00', dark: '#991A00' }, + yellow: { background: '#FEF6D0', border: '#863B05', dark: '#863B05' }, + teal: { background: '#EAF5F4', border: '#56C6BE', dark: '#2B91B8' }, + violet: { background: '#F0EAFF', border: '#A988E6', dark: '#7F69B5' }, + rose: { background: '#F5E8ED', border: '#E9726B', dark: '#B34B5F' }, + amber: { background: '#F7F1E4', border: '#E7B04A', dark: '#A4751F' }, + cyan: { background: '#DFF3F9', border: '#77B6E3', dark: '#649EAF' }, + brand: { background: '#E4F8FB', border: '#BCC7F4', dark: '#BCC7F4' }, }; -function getColorEntry(color: any): { r: number; g: number; b: number; dark: string } | null { - if (!color || typeof color !== 'string') return null; +export function updateColor(color: any, newOpacity: number) { + if (!color || typeof color !== 'string') return color; - const named = COLOR_MAP[color.toLowerCase()]; - if (named) return named; + const entry = COLOR_MAP[color.toLowerCase()]; + if (entry) { + // For named colors, return the border shade at the given opacity for backward compat + return entry.border; + } // Fallback: try rgba format const rgbaMatch = color.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*[\d.]+)?\)$/); if (rgbaMatch) { - const r = Number(rgbaMatch[1]); - const g = Number(rgbaMatch[2]); - const b = Number(rgbaMatch[3]); - // Darken by 40% for text - const dark = `rgb(${Math.round(r * 0.5)}, ${Math.round(g * 0.5)}, ${Math.round(b * 0.5)})`; - return { r, g, b, dark }; + const [, r, g, b] = rgbaMatch; + return `rgba(${r}, ${g}, ${b}, ${newOpacity})`; } - return null; -} - -export function updateColor(color: any, newOpacity: number) { - const entry = getColorEntry(color); - if (!entry) return color; - return `rgba(${entry.r}, ${entry.g}, ${entry.b}, ${newOpacity})`; + return color; } export function getChipColors(color: any): { background: string; border: string; text: string; icon: string } { - const entry = getColorEntry(color); - if (!entry) { + if (!color || typeof color !== 'string') { return { background: 'transparent', border: color, text: color, icon: color }; } - return { - background: `rgba(${entry.r}, ${entry.g}, ${entry.b}, 0.1)`, - border: `rgba(${entry.r}, ${entry.g}, ${entry.b}, 0.3)`, - text: entry.dark, - icon: entry.dark, - }; + const entry = COLOR_MAP[color.toLowerCase()]; + if (entry) { + return { + background: entry.background, + border: entry.border, + text: entry.dark, + icon: entry.dark, + }; + } + + // Fallback for unrecognized colors + return { background: 'transparent', border: color, text: color, icon: color }; }