Files
emaui/libs/shared/src/lib/theme/status-tone.ts
fitse-yotor b63e07305a feat(theme): add semantic tokens, focus ring, and reduced-motion support
Adds the layer feature code has been missing: tokens that name a role — page
surface, subtle border, danger — instead of a colour. Every one resolves to a
Mantine variable rather than a literal, so they follow the colour scheme for
free and cannot drift from the theme. A parallel palette of raw hexes would
have recreated exactly the problem this exists to fix.

Also closes three accessibility gaps that had no implementation anywhere in the
codebase: no :focus-visible rule, no screen-reader-only utility, and no global
prefers-reduced-motion handling.

The focus work found a real bug, and nearly introduced a worse one. Mantine
already rings its own controls, so the first attempt deferred to it with
`outline: none` on .mantine-focus-auto. That suppressed Mantine's ring without
replacing it, leaving portal buttons with no focus indicator at all — and it
only showed up in one app, because which rule won depended on stylesheet order.
The rules use identical values, so overlapping them is invisible and safe;
opting out is not. The focus test now asserts computed outline width and style,
not just pixels, since a screenshot alone would not have caught this.

`status-tone.ts` establishes the six-tone vocabulary that the 48 scattered
status→colour maps will eventually collapse onto. Nothing consumes it yet.

No visual change: the 8 existing baselines pass unmodified.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 12:11:20 +03:00

52 lines
1.4 KiB
TypeScript

/**
* The platform's status vocabulary.
*
* There are 48 separate status→colour maps across the codebase, each deciding
* independently what "pending" looks like. They disagree. The fix is not one
* bigger map — domain statuses genuinely differ per feature — but one small set
* of *tones* that every domain maps onto, so the colour decision is made six
* times instead of forty-eight.
*
* `semantic.css` carries the CSS-variable form of these for stylesheet use.
* This module is for the many places that need a Mantine `color` prop instead.
*/
export type StatusTone =
| 'success'
| 'warning'
| 'danger'
| 'info'
| 'pending'
| 'neutral';
/**
* Tone → Mantine colour name.
*
* Deliberately the only place a tone becomes a colour. Changing the platform's
* idea of "warning" is an edit here, not a sweep through 48 files.
*/
export const STATUS_TONE_COLOR: Record<StatusTone, string> = {
success: 'green',
warning: 'yellow',
danger: 'red',
info: 'blue',
pending: 'orange',
neutral: 'gray',
};
/**
* Tone → CSS custom properties, for inline styles and stylesheets.
*
* Returns variable references rather than resolved colours so the values keep
* following the active colour scheme.
*/
export function statusToneVars(tone: StatusTone): {
color: string;
background: string;
} {
return {
color: `var(--ema-status-${tone}-fg)`,
background: `var(--ema-status-${tone}-bg)`,
};
}