Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion data/menuItems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export const menuItems: MenuItem[] = [
header: {
title: 'AI Professionals',
subtitle: 'Expert advisors for every need',
gradient: 'bg-gradient-to-r from-blue-50 to-purple-50',
gradient: 'bg-action-tint',
},
},
},
Expand Down
77 changes: 17 additions & 60 deletions data/professionals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -334,64 +334,21 @@ export const professionalCategories = {
} as const;

/**
* Get accent color class for Tailwind
* Accent classes for a first-party professional.
*
* There is one scheme, because there is one brand. These six ARE Botsmann, so
* they wear the identity defined in app/globals.css rather than a per-person
* colour -- a blue Lex beside an ochre CTA was the whole problem.
*
* The `color` argument is kept so callers and the data shape stay unchanged;
* it no longer selects a palette.
*/
export const getAccentColorClasses = (color: ProfessionalAccentColor) => {
const colors = {
blue: {
bg: 'bg-blue-500',
bgLight: 'bg-blue-100',
bgGradient: 'from-blue-500 to-blue-600',
text: 'text-blue-600',
border: 'border-blue-500',
hover: 'hover:bg-blue-600',
groupHoverText: 'group-hover:text-blue-600',
},
green: {
bg: 'bg-green-500',
bgLight: 'bg-green-100',
bgGradient: 'from-green-500 to-green-600',
text: 'text-green-600',
border: 'border-green-500',
hover: 'hover:bg-green-600',
groupHoverText: 'group-hover:text-green-600',
},
indigo: {
bg: 'bg-indigo-500',
bgLight: 'bg-indigo-100',
bgGradient: 'from-indigo-500 to-indigo-600',
text: 'text-indigo-600',
border: 'border-indigo-500',
hover: 'hover:bg-indigo-600',
groupHoverText: 'group-hover:text-indigo-600',
},
red: {
bg: 'bg-red-500',
bgLight: 'bg-red-100',
bgGradient: 'from-red-500 to-red-600',
text: 'text-red-600',
border: 'border-red-500',
hover: 'hover:bg-red-600',
groupHoverText: 'group-hover:text-red-600',
},
amber: {
bg: 'bg-amber-500',
bgLight: 'bg-amber-100',
bgGradient: 'from-amber-500 to-amber-600',
text: 'text-amber-600',
border: 'border-amber-500',
hover: 'hover:bg-amber-600',
groupHoverText: 'group-hover:text-amber-600',
},
purple: {
bg: 'bg-purple-500',
bgLight: 'bg-purple-100',
bgGradient: 'from-purple-500 to-purple-600',
text: 'text-purple-600',
border: 'border-purple-500',
hover: 'hover:bg-purple-600',
groupHoverText: 'group-hover:text-purple-600',
},
};
return colors[color];
};
export const getAccentColorClasses = (_color?: ProfessionalAccentColor) => ({
bg: 'bg-action',
bgLight: 'bg-action-tint',
bgGradient: 'from-action to-action-hover',
text: 'text-action',
border: 'border-action',
hover: 'hover:bg-action-hover',
groupHoverText: 'group-hover:text-action',
});
25 changes: 22 additions & 3 deletions tests/__tests__/lib/design-tokens.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,19 @@ const CUSTOM_BOT_SURFACES = [
join('lib', 'config', 'colors.ts'),
];

/** Semantic status colours are a separate scale from the brand accent. */
const SEMANTIC_FILES = [join('components', 'knowledge', 'Callout.tsx')];
/**
* Categorical and semantic scales, which are NOT the brand accent.
*
* A status scale has to stay mutually distinguishable: recolouring
* "processing" to ochre would make it read as brand chrome rather than state.
* Same for provider identity, which exists so you can tell Ollama from OpenAI
* at a glance. Each entry is an exception with a reason, not a to-do.
*/
const SEMANTIC_FILES = [
join('components', 'knowledge', 'Callout.tsx'), // info / warning / error
join('lib', 'constants.ts'), // document status: pending / processing / ready / error
join('lib', 'infrastructure', 'providers.ts'), // per-provider identity colours
];

function tsxFiles(dir: string): string[] {
const out: string[] = [];
Expand All @@ -59,7 +70,15 @@ function isAllowed(rel: string): boolean {
}

describe('design tokens', () => {
const files = [...tsxFiles(join(ROOT, 'app')), ...tsxFiles(join(ROOT, 'components'))];
// data/ and lib/ too: the palette hid in data/professionals.ts for a whole
// sweep because the first version of this guard only looked at app/ and
// components/. A scanner is blind in exactly the shapes it forgets to read.
const files = [
...tsxFiles(join(ROOT, 'app')),
...tsxFiles(join(ROOT, 'components')),
...tsxFiles(join(ROOT, 'data')),
...tsxFiles(join(ROOT, 'lib')),
];

it('scans a meaningful number of files', () => {
expect(files.length).toBeGreaterThan(100);
Expand Down
Loading