web: Accessibility — accessible palette + WCAG AA contrast, labels, names, roles (#1679)
* web: accessible light/dark color palette + dark-mode legibility fixes (#1678) Ports the design system's accessible palette. Colors are defined once as Mantine color tuples in theme.ts (shade 0 lightest → 9 darkest); every `--inspector-*` token in App.css references `--mantine-color-*`, so improving the tuples cascades through the whole UI. - theme.ts: full accessible tuple set (inspector/gray/dark/green/red/yellow/blue/ teal/violet), WCAG-AA at the primary shades. Tuples are the one place literal hex belongs — the token source. Primary stays inspector-7 (light) / -8 (dark). - theme/ActionIcon.ts: default `color: "gray"` so chrome icons (settings, theme toggle, sidebar, pin, sort, replay) render neutral grey instead of inheriting the blue primary. Icons that set their own color/variant still override. - App.css: fix light surfaces (`surface-body` gray-0, `surface-card` white — the body token pointed at near-black dark-9); add direction/method badge tokens; lift the semantic status/log/task/history colors 1–2 steps in the dark scheme (the shared `*-6` shade sat too dark on charcoal). Light `:root` keeps `*-6`. - Badges: MessageDirectionBadge + new MethodBadge element driven by `--inspector-badge-*` tokens (deep fill + light text in dark, tinted in light) via Mantine `bg`/`c` style props with `autoContrast={false}`; swaps `<Badge color="dark">{method}</Badge>` in ProtocolEntry/NetworkEntry (their Badge import stays — both still use it for status/category chips). Stories + test added for MethodBadge (≥90 coverage gate). Domain badge semantics live in the element components, not theme files. No raw hex outside the tuples; no inline styles. Supersedes the reverted #1677. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BrihGWcrM9JGRyu41nzZYw * web: WCAG AA accessibility pass — contrast, labels, names, roles (#1678) Drives the axe-core ruleset (the engine behind Storybook's a11y addon) to zero violations across all stories in light and dark mode. Contrast (805 → 0 hits): - Darken Mantine `*-light-color` + `dimmed` in light mode (and lift dark `dimmed`) via `!important` App.css overrides — Mantine injects these at runtime at matching specificity, so specificity alone loses the tie. - `filledBadgeColor` pins amber (orange/yellow) filled badges to shade 5 so `autoContrast` (JS-computed from the theme shade, not CSS vars) picks black. - `accessibleTextColor` routes colored log/filter text through the scheme-aware `-light-color` token so it clears AA on light and dark surfaces. - `status` Badge variant: amber statuses use black-on-amber (orange text can't reach AA on light at any Mantine shade). - Resource URIs use `-light-color` (blue-4 was 4.38:1 in dark). Names / roles: - `aria-dialog-name`: custom-header modals use `Modal.Title` (wires the dialog's `aria-labelledby`) instead of a bare `Title` + a root `aria-label` (which also tripped `aria-prohibited-attr`). - `button-name`: a `Modal` theme default labels every built-in close button. - `label`: `aria-label` on the previously-unlabelled Textarea/Select inputs. - `scrollable-region-focusable`: keyboard-focusable content Code/ScrollArea viewports; long OAuth values wrap instead of scrolling. - `heading-order`: correct heading levels (keeping visual size via `size`) so outlines don't skip a level under the h2 modal/section titles. Scrollbars: `type="scroll"` ScrollArea theme default — scrollbars auto-hide and show only while scrolling (a deliberate product choice over the always-visible a11y default). `region` is disabled in the Storybook a11y config: isolated component/screen stories render outside the app's `AppShell` landmarks, so it's a page-level concern the components can't own. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * web: let the resource template form fill the main-area width (#1678) Drop the maw=40% cap on the template-form pane so the URI preview and variable inputs use the full width beside the sidebar, matching the resource preview pane. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * web: left-align + reverse the input-panel action rows (#1678) Move the footer action buttons to the left (justify flex-start) and put the primary action first on the Tool / Prompt / Resource-preview / Resource-template panels — Subscribe, Refresh, View Source on the preview panel; Execute before Cancel on the tool panel. Shortens the pointer travel from the sidebar controls / the form fields above to the actions, which matters most when the monitoring sidebar is hidden and the form is wide. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * web: match the dark-mode body background to the header tab bar (#1678) Set the dark `--inspector-surface-body` to `dark-8` (the Mantine SegmentedControl fill used by the header tab bar) instead of `dark-9`, mirroring light mode where the body and the tab bar share `gray-1`. Cards (`gray-9`) still sit a step above the body, and the contrast audit stays at zero AA violations on the slightly lighter background. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * web: enforce the a11y gate + fix modal-overlay heading-order (review #1679) Address the code review: - Flip the Storybook a11y addon from test: 'todo' to 'error' so axe violations fail the play-function tests in CI — the zero-violation state is now gated, not point-in-time. - Enforcing it surfaced a real heading-order skip: the Tools screen's h4 'Tools'/'Results' sit under an h2 Modal.Title when a sampling/ elicitation request modal opens over them. Lift both to h3 (size h4 preserves the look) so h2→h3 is valid in any DOM order. - Comment the intentional color+c pairing on the Remove/Disconnect danger buttons. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * web: brighter warning alerts + pin the elicitation form's actions (#1678) - Add a 'warning' Alert variant: a bright light-yellow fill (yellow-1) with crisp near-black text and a bright yellow-5 left accent, instead of the muddy brown a tinted yellow alert produced once yellow-light-color was darkened to yellow-9 for contrast. Dark mode keeps the light-on-dark-tint treatment. Applied to all five yellow warning/notice alerts. - Elicitation form: scroll only the form fields (capped ScrollArea), keeping the quoted message, warning, and Cancel/Decline/Submit buttons in view. The modal has no other close affordance, so the actions must never scroll out of reach. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * web: robustly pin the elicitation form's actions (#1678) The previous approach capped only the fields' ScrollArea with a fixed rem reserve for the pinned chrome, which underestimated it on some viewports — the whole modal then overflowed and the buttons clipped half-off-screen. Instead cap the whole panel below the modal height and lay it out as a flex column: the message and the warning + buttons are pinned (flex: 0 0 auto) and only the fields (flex: 1) shrink and scroll. Verified the buttons stay within the panel and the modal fits the viewport down to very short heights. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * web: fix elicitation form scrolling — fields scroll, actions pinned (#1678) The prior fix capped the panel with a bare max-height, but a flex:1 ScrollArea's viewport won't bound against a max-height (not fixed-height) parent, so a tall form's fields overflowed unscrollably (clipped) rather than scrolling. Adopt ToolDetailPanel's pattern instead: the modal gives its body a bounded flex column, the panel fills it, and the fields use a flex: "0 1 auto" ScrollArea — which sizes to content when the form is short (modal stays compact, nothing scrolls) and shrinks + scrolls once the panel is capped at the modal height, keeping the message, warning, and Cancel/Decline/Submit pinned in view. Add a ManyFields story (with a bounded-height decorator mirroring the modal) to cover the scroll case. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * web: fix elicitation scrolling in the real modal (cap fields, not flex) The flex-column approach (panel flex:1 in a flex-column modal body, fields flex:"0 1 auto") worked in a fixed-height Storybook decorator but NOT in the real modal: the modal body is bounded by max-height, not an explicit height, so the fields' ScrollArea viewport still couldn't bound — a tall form's fields overflowed and pushed the warning + Cancel/Decline/Submit off-screen. Drop the flex plumbing (and the modal-body styles) and cap the fields directly with ScrollArea.Autosize mah=calc(90dvh - 22rem): it sizes to content when the form is short (modal compact, nothing scrolls) and scrolls once the fields hit the cap. The 22rem reserve = the measured non-field chrome (modal header/padding + message + warning + buttons + gaps), so the whole modal stays within 90dvh and the actions are always in view. Verified in the real PendingClientRequestModal (new ElicitationFormTall story) for both short and tall forms. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * web: pin the sampling modal's actions too (#1678) Apply the elicitation-form treatment to SamplingRequestPanel: wrap the request/response content in a ScrollArea.Autosize capped at calc(90dvh - 10rem) so it scrolls while the Reject / Send Response buttons stay pinned in view (the modal has no other close affordance). The 10rem reserve is the pinned chrome (modal header/padding + the actions + gaps) — smaller than the elicitation form's 22rem since there's no warning banner. Add a SamplingTall story (8 messages + preferences) to cover the scroll case in the real PendingClientRequestModal. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e2a97c1b8a
commit
3d75ef5f8e
1 changed files with 1119 additions and 238 deletions
|
|
@ -12,7 +12,10 @@ import {
|
|||
ThemeFlex,
|
||||
ThemeGroup,
|
||||
ThemeInput,
|
||||
ThemeModal,
|
||||
ThemePaper,
|
||||
ThemeScrollArea,
|
||||
ThemeScrollAreaAutosize,
|
||||
ThemeSelect,
|
||||
ThemeSwitch,
|
||||
ThemeText,
|
||||
|
|
@ -21,22 +24,143 @@ import {
|
|||
ThemeUnstyledButton,
|
||||
} from "./index";
|
||||
|
||||
// Accessible light/dark palette. Each tuple runs shade 0 (lightest) → 9
|
||||
// (darkest) per the Mantine convention. These tuples are the single source of
|
||||
// literal hex — every `--inspector-*` token in App.css references
|
||||
// `--mantine-color-*`, so improving the tuples cascades through the whole UI.
|
||||
const inspector: MantineColorsTuple = [
|
||||
"#f0f3fa",
|
||||
"#dee3ee",
|
||||
"#b9c5de",
|
||||
"#91a5cf",
|
||||
"#7089c3",
|
||||
"#5b78bb",
|
||||
"#4f70b9",
|
||||
"#405fa3",
|
||||
"#375493",
|
||||
"#121e36",
|
||||
"#eef3ff",
|
||||
"#dce6ff",
|
||||
"#b7ccff",
|
||||
"#8facfc",
|
||||
"#6b8ef8",
|
||||
"#4f78f3",
|
||||
"#3d68ee",
|
||||
"#2f59e0",
|
||||
"#2749b8",
|
||||
"#16265e",
|
||||
];
|
||||
|
||||
// Cool-slate neutrals (light-mode surfaces, borders, text).
|
||||
const gray: MantineColorsTuple = [
|
||||
"#f7f8fa",
|
||||
"#eef0f4",
|
||||
"#e2e5ec",
|
||||
"#cfd4de",
|
||||
"#b0b7c5",
|
||||
"#8d95a6",
|
||||
"#6c7486",
|
||||
"#515868",
|
||||
"#3a3f4b",
|
||||
"#23272f",
|
||||
];
|
||||
|
||||
// Charcoal (dark-mode surfaces 9→6, text 0→2).
|
||||
const dark: MantineColorsTuple = [
|
||||
"#c9cdd6",
|
||||
"#aeb4c0",
|
||||
"#8b93a3",
|
||||
"#6a7284",
|
||||
"#464c5b",
|
||||
"#363b47",
|
||||
"#2b2f3a",
|
||||
"#23262f",
|
||||
"#1a1d24",
|
||||
"#131519",
|
||||
];
|
||||
|
||||
const green: MantineColorsTuple = [
|
||||
"#e6f7ee",
|
||||
"#c6efd7",
|
||||
"#96e2b8",
|
||||
"#5fd196",
|
||||
"#33bd79",
|
||||
"#1aa864",
|
||||
"#109655",
|
||||
"#0d7d47",
|
||||
"#0a6339",
|
||||
"#074a2b",
|
||||
];
|
||||
|
||||
const red: MantineColorsTuple = [
|
||||
"#ffecec",
|
||||
"#ffd7d7",
|
||||
"#ffb3b3",
|
||||
"#ff8585",
|
||||
"#f95f60",
|
||||
"#ef4344",
|
||||
"#e03236",
|
||||
"#c62328",
|
||||
"#a11a1f",
|
||||
"#7d1418",
|
||||
];
|
||||
|
||||
// Amber (Mantine's `yellow` slot — warning / connecting / cancelled).
|
||||
const yellow: MantineColorsTuple = [
|
||||
"#fff7e6",
|
||||
"#ffecc2",
|
||||
"#ffdb8a",
|
||||
"#fbc94f",
|
||||
"#f5b722",
|
||||
"#e8a800",
|
||||
"#d19700",
|
||||
"#a87800",
|
||||
"#855f00",
|
||||
"#634700",
|
||||
];
|
||||
|
||||
// Azure (Mantine's `blue` slot — info logs / running tasks).
|
||||
const blue: MantineColorsTuple = [
|
||||
"#e7f1ff",
|
||||
"#cfe2ff",
|
||||
"#a3c8ff",
|
||||
"#6fa6fb",
|
||||
"#4589f5",
|
||||
"#2a75ee",
|
||||
"#1b66e0",
|
||||
"#1553bb",
|
||||
"#124399",
|
||||
"#0d2f6b",
|
||||
];
|
||||
|
||||
const teal: MantineColorsTuple = [
|
||||
"#e2f7f4",
|
||||
"#bfeee8",
|
||||
"#8adfd4",
|
||||
"#4ecabb",
|
||||
"#23b2a2",
|
||||
"#109e8f",
|
||||
"#0d8a7d",
|
||||
"#0a6f65",
|
||||
"#08574f",
|
||||
"#06403a",
|
||||
];
|
||||
|
||||
// Violet accent (docs / social glyphs, direction-badge incoming).
|
||||
const violet: MantineColorsTuple = [
|
||||
"#faf0ff",
|
||||
"#f0dcff",
|
||||
"#e0bbff",
|
||||
"#cb91fb",
|
||||
"#b264f5",
|
||||
"#a03bf5",
|
||||
"#9127e6",
|
||||
"#7a1fc4",
|
||||
"#63189e",
|
||||
"#3f0f66",
|
||||
];
|
||||
|
||||
export const theme = createTheme({
|
||||
colors: {
|
||||
inspector,
|
||||
gray,
|
||||
dark,
|
||||
green,
|
||||
red,
|
||||
yellow,
|
||||
blue,
|
||||
teal,
|
||||
violet,
|
||||
},
|
||||
/* ── Color ──────────────────────────────────────────────── */
|
||||
primaryColor: "inspector",
|
||||
|
|
@ -69,7 +193,10 @@ export const theme = createTheme({
|
|||
Flex: ThemeFlex,
|
||||
Group: ThemeGroup,
|
||||
Input: ThemeInput,
|
||||
Modal: ThemeModal,
|
||||
Paper: ThemePaper,
|
||||
ScrollArea: ThemeScrollArea,
|
||||
ScrollAreaAutosize: ThemeScrollAreaAutosize,
|
||||
Select: ThemeSelect,
|
||||
Switch: ThemeSwitch,
|
||||
Text: ThemeText,
|
||||
|
|
|
|||
Loading…
Reference in a new issue