Skip to content

Commit a899edf

Browse files
catomeanclaude
andcommitted
fix(design): the homepage had a fifth colour system, and the guard missed it
#148 shipped with the guard passing and the homepage still emitting bg-blue-100, text-indigo-600, bg-purple-100. Both were true, because the guard only walked app/ and components/. The palette was in data/professionals.ts -- its own getAccentColorClasses, giving each of the six first-party professionals a colour (blue, green, indigo, red, amber, purple). Same mistake as the bots in #144, in the one directory the scanner did not read. That is the whole lesson: a source scanner is blind in exactly the shapes you forget to look at, so its first clean run proves nothing. - the six professionals now use the brand, like the six bots - the guard walks data/ and lib/ too - data/menuItems.ts lost a blue/purple gradient Two files are now DOCUMENTED exceptions rather than debt, because their colour is categorical and has to stay mutually distinguishable: lib/constants.ts document status -- pending yellow, processing blue, ready green, error red. Recolouring "processing" to ochre would make state read as brand chrome. lib/infrastructure/providers.ts per-provider identity, so Ollama and OpenAI are tellable apart at a glance. Both sit alongside Callout.tsx's info/warning/error triad, each with its reason written next to it. verify: format, lint, typecheck, 256 tests, build -- all green. Homepage confirmed to emit no raw palette class at all. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HVwg8DKHQktxJuHeLM3xpG
1 parent 382eebb commit a899edf

3 files changed

Lines changed: 40 additions & 64 deletions

File tree

data/menuItems.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export const menuItems: MenuItem[] = [
6464
header: {
6565
title: 'AI Professionals',
6666
subtitle: 'Expert advisors for every need',
67-
gradient: 'bg-gradient-to-r from-blue-50 to-purple-50',
67+
gradient: 'bg-action-tint',
6868
},
6969
},
7070
},

data/professionals.ts

Lines changed: 17 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -334,64 +334,21 @@ export const professionalCategories = {
334334
} as const;
335335

336336
/**
337-
* Get accent color class for Tailwind
337+
* Accent classes for a first-party professional.
338+
*
339+
* There is one scheme, because there is one brand. These six ARE Botsmann, so
340+
* they wear the identity defined in app/globals.css rather than a per-person
341+
* colour -- a blue Lex beside an ochre CTA was the whole problem.
342+
*
343+
* The `color` argument is kept so callers and the data shape stay unchanged;
344+
* it no longer selects a palette.
338345
*/
339-
export const getAccentColorClasses = (color: ProfessionalAccentColor) => {
340-
const colors = {
341-
blue: {
342-
bg: 'bg-blue-500',
343-
bgLight: 'bg-blue-100',
344-
bgGradient: 'from-blue-500 to-blue-600',
345-
text: 'text-blue-600',
346-
border: 'border-blue-500',
347-
hover: 'hover:bg-blue-600',
348-
groupHoverText: 'group-hover:text-blue-600',
349-
},
350-
green: {
351-
bg: 'bg-green-500',
352-
bgLight: 'bg-green-100',
353-
bgGradient: 'from-green-500 to-green-600',
354-
text: 'text-green-600',
355-
border: 'border-green-500',
356-
hover: 'hover:bg-green-600',
357-
groupHoverText: 'group-hover:text-green-600',
358-
},
359-
indigo: {
360-
bg: 'bg-indigo-500',
361-
bgLight: 'bg-indigo-100',
362-
bgGradient: 'from-indigo-500 to-indigo-600',
363-
text: 'text-indigo-600',
364-
border: 'border-indigo-500',
365-
hover: 'hover:bg-indigo-600',
366-
groupHoverText: 'group-hover:text-indigo-600',
367-
},
368-
red: {
369-
bg: 'bg-red-500',
370-
bgLight: 'bg-red-100',
371-
bgGradient: 'from-red-500 to-red-600',
372-
text: 'text-red-600',
373-
border: 'border-red-500',
374-
hover: 'hover:bg-red-600',
375-
groupHoverText: 'group-hover:text-red-600',
376-
},
377-
amber: {
378-
bg: 'bg-amber-500',
379-
bgLight: 'bg-amber-100',
380-
bgGradient: 'from-amber-500 to-amber-600',
381-
text: 'text-amber-600',
382-
border: 'border-amber-500',
383-
hover: 'hover:bg-amber-600',
384-
groupHoverText: 'group-hover:text-amber-600',
385-
},
386-
purple: {
387-
bg: 'bg-purple-500',
388-
bgLight: 'bg-purple-100',
389-
bgGradient: 'from-purple-500 to-purple-600',
390-
text: 'text-purple-600',
391-
border: 'border-purple-500',
392-
hover: 'hover:bg-purple-600',
393-
groupHoverText: 'group-hover:text-purple-600',
394-
},
395-
};
396-
return colors[color];
397-
};
346+
export const getAccentColorClasses = (_color?: ProfessionalAccentColor) => ({
347+
bg: 'bg-action',
348+
bgLight: 'bg-action-tint',
349+
bgGradient: 'from-action to-action-hover',
350+
text: 'text-action',
351+
border: 'border-action',
352+
hover: 'hover:bg-action-hover',
353+
groupHoverText: 'group-hover:text-action',
354+
});

tests/__tests__/lib/design-tokens.test.ts

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,19 @@ const CUSTOM_BOT_SURFACES = [
3737
join('lib', 'config', 'colors.ts'),
3838
];
3939

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

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

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

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

0 commit comments

Comments
 (0)