diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index 0dd0ed0d..3dae9ab7 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -75,6 +75,7 @@ const preview: Preview = { "Helpers", "Theme", "Theme/Logos", + "Accessibility", "MUI", "Components", ], diff --git a/src/storybook/accessibility/00-overview.mdx b/src/storybook/accessibility/00-overview.mdx new file mode 100644 index 00000000..69d5251e --- /dev/null +++ b/src/storybook/accessibility/00-overview.mdx @@ -0,0 +1,175 @@ +import { Meta } from "@storybook/blocks"; + + + +
+ +# Accessibility + +Accessibility (a11y) in the Diamond Design System is a baseline for quality, +clarity, and usability in complex scientific tools, not a checklist or a +separate mode. + +Our goal is to make interfaces that are understandable, predictable, and +usable across a wide range of abilities, environments, and levels of fatigue. + +## Standards and approach + +We follow WCAG 2.2 as the baseline for accessibility compliance. + +We also use APCA (Advanced Perceptual Contrast Algorithm) to design for +perceived readability. Unlike WCAG contrast ratios, APCA accounts for font +size, weight, and light/dark conditions. + +WCAG defines minimum thresholds. APCA is used to ensure interfaces remain +readable, comfortable, and usable in real-world conditions. + +This reflects the direction of emerging standards such as WCAG 3. + +## Core principles + + + +## ✅ Do + +### Interactive elements + + + +### Keyboard navigation + + + +For composite components (e.g. menus, dialogs, tables), manage focus +intentionally: + + + +### Icons and icon-only actions + + + +For interactive icons, ensure both an accessible name and a visible affordance +such as a tooltip or label. + +In MUI, use aria-label on IconButton. Tooltips (e.g. + +<Tooltip />) should be used in addition to, not instead of, +accessible labelling. + +### Forms and inputs + + + +### Content and layout + + + +## ❌ Don’t + + + +## Storybook guidance + + + +## Quick sense check + + + +
diff --git a/src/storybook/accessibility/01-colour-contrast.mdx b/src/storybook/accessibility/01-colour-contrast.mdx new file mode 100644 index 00000000..617086b5 --- /dev/null +++ b/src/storybook/accessibility/01-colour-contrast.mdx @@ -0,0 +1,782 @@ +import { Meta } from "@storybook/blocks"; + + + + + +
+ +# Colour contrast and vision + +Colour contrast directly affects readability, speed, and error rate. In dense, +data-heavy scientific interfaces, poor contrast increases cognitive load and +slows decision-making. Colour vision deficiency adds a second, related +failure mode: a colour choice can be readable and still fail if the hues +involved aren't reliably distinguishable in the first place. + +We prioritise perceptual contrast using APCA when defining colour choices, +while maintaining WCAG 2.2 as the compliance baseline. We also check those +same colour choices under colour vision deficiency simulation, since a +pairing can clear both APCA and WCAG 2.2 and still rely on a hue distinction +some users can't actually perceive. + +## Why contrast needs a modern approach + +Traditional WCAG 2.x contrast ratios are useful, but they are based on +luminance ratios rather than perceived readability. In practice, some +combinations can technically pass while still feel strained or unstable in +real UI. + +This becomes more visible across different font sizes, weights, and light or +dark environments. + +This leads to common issues: + + + +APCA helps address these limitations by modelling perceived readability rather +than relying only on raw luminance difference. + +## APCA contrast (primary) + +APCA reflects how contrast is actually experienced by users more closely than +older ratio-based methods. + + + +We use APCA to tune token values, component defaults, and interaction states. + +## WCAG 2.2 contrast (baseline) + +WCAG 2.2 remains the current compliance baseline for text contrast. + + + +WCAG 2.2 is necessary, but on its own it is not enough to judge readability +quality. + +## How we use them together + +Contrast decisions follow a clear order: + + + +This helps keep interfaces both compliant and genuinely usable. + +## Colour-specific considerations + +Some hues behave less predictably for perceived contrast, even when WCAG 2.x +ratios look acceptable. + +In DiamondDS this matters most for status colours: `--ds-success`, +`--ds-warning`, and `--ds-danger`. + +A common issue in real interfaces is that black text on a saturated colour may +look mathematically strong, but visually feel unstable, vibrating, or tiring, +especially for smaller or denser text. + +APCA is better at exposing these perception problems. + + + +When using these colours: + + + +## Colour blindness + +Colour vision deficiency (CVD) is not one condition: + + + +Together, the red-green deficiencies (protan and deutan) are by far the most +common, which matters directly here: `--ds-success`, `--ds-warning`, and +`--ds-danger` sit in exactly the green, amber, and red range those +deficiencies compress together. + +### Checked, not assumed + +WCAG 2.2's success criterion 1.4.1 (Use of Colour) already requires that +colour is never the only way information is conveyed. Confirming that means +actually checking what these tokens look like under a colour vision +deficiency, not assuming different hues are automatically distinguishable. +Running DiamondDS's status colours through a standard CVD simulation +(Machado, Oliveira and Fernandes, 2009) shows a real gap, not a hypothetical +one: + +
+
+
+

Light: under CVD simulation

+ {[ + [ + "Main", + "text", + [ + ["As designed", "#137329", "#a95703", "#ae1e13"], + ["Simulated: protanopia", "#756821", "#706100", "#4e450f"], + ["Simulated: deuteranopia", "#6a602f", "#837401", "#716406"], + ], + ], + [ + "Solid", + "chip", + [ + ["As designed", "#1b8834", "#e97b12", "#d63c41"], + ["Simulated: protanopia", "#8a7b2b", "#9c8900", "#686040"], + ["Simulated: deuteranopia", "#7e733b", "#b6a20f", "#8f813c"], + ], + ], + [ + "Container", + "chip", + [ + ["As designed", "#d4f3df", "#fef0df", "#fde7e5"], + ["Simulated: protanopia", "#f3eede", "#f5f0de", "#ebe9e5"], + ["Simulated: deuteranopia", "#edeae0", "#f9f3df", "#f0ede5"], + ], + ], + ].map(([roleName, variant, rows]) => ( +
+

+ {roleName} +

+ {rows.map(([label, success, warning, danger]) => ( +
+

{label}

+ {variant === "text" ? ( +
+ Success + Warning + Danger +
+ ) : ( +
+
+ Success +
+
+ Warning +
+
+ Danger +
+
+ )} +
+ ))} +
+ ))} +
+
+

Dark: under CVD simulation

+ {[ + [ + "Main", + "text", + [ + ["As designed", "#6fd88a", "#ffb067", "#ff9088"], + ["Simulated: protanopia", "#d9c985", "#c9b660", "#a9a187"], + ["Simulated: deuteranopia", "#cabf8f", "#dbc868", "#c4b886"], + ], + ], + [ + "Solid", + "chip", + [ + ["As designed", "#23913c", "#f07a13", "#d63c41"], + ["Simulated: protanopia", "#938434", "#9d8a00", "#686040"], + ["Simulated: deuteranopia", "#867b43", "#b9a50e", "#8f813c"], + ], + ], + [ + "Container", + "chip", + [ + ["As designed", "#10341a", "#382006", "#3a1613"], + ["Simulated: protanopia", "#342f18", "#282204", "#1f1c13"], + ["Simulated: deuteranopia", "#302c1c", "#2d2806", "#282412"], + ], + ], + ].map(([roleName, variant, rows]) => ( +
+

+ {roleName} +

+ {rows.map(([label, success, warning, danger]) => ( +
+

{label}

+ {variant === "text" ? ( +
+ Success + Warning + Danger +
+ ) : ( +
+
+ Success +
+
+ Warning +
+
+ Danger +
+
+ )} +
+ ))} +
+ ))} +
+
+
+

What the simulation shows

+
    +
  • + Under protanopia, main-role success and warning both land on a + similar dark olive: about 34 points apart in simulated colour, + closer than most people would reliably tell apart at a glance. +
  • +
  • + Under deuteranopia, the solid role gets even closer: success and + danger sit only about 22 points apart, both a similar muddy + olive-brown. +
  • +
  • + Container is the worst case. It's already the palest role, so CVD + simulation collapses all three towards the same washed-out beige: + as close as 3-15 points apart depending on the pair and the + deficiency. An outlined button or alert using container colour + alone is close to unreadable as a status signal under CVD. +
  • +
  • + Dark mode isn't a safer version of this. Main-role success and + warning already have almost the same luminance to begin with + (0.543 vs 0.533), so lightness, usually the fallback that survives + CVD, doesn't help separate them there either. Solid role is worse + in dark mode than light: under deuteranopia, success and danger + land only about 13 points apart, tighter than any pairing in light + mode. +
  • +
+
+ +
+ +### What this means in practice + + + +This is a current gap in the token values, not something already handled, +worth raising with whoever owns the palette rather than assumed solved. + +### Why APCA, not WCAG ratio alone + +The clearest case of this in DiamondDS is the Warning button. Judged purely by +WCAG 2.2's contrast ratio, black text scores _higher_ on the warning surface +than white text does: it's the pairing the ratio math would point you +towards. In practice it's the one that vibrates and strains against the +saturated orange. APCA's polarity-aware model agrees with what the eye +actually sees, which is why the token ships white text instead: + +
+
+
+

Light: Warning button

+
+
+ --ds-warning-solid: #e97b12 +
+
+
+

+ WCAG 2.2 would favour this: 5.9:1, clears AA +

+
+ Beamline temperature near limit +
+

APCA Lc ≈ 48, reads as strained

+
+
+

+ What we ship: 2.9:1, fails AA on the ratio alone +

+
+ Beamline temperature near limit +
+

APCA Lc ≈ 60, reads as legible

+
+
+
+
+
+

Dark: Warning button

+
+
+ --ds-warning-solid: #f07a13 +
+
+
+

+ WCAG 2.2 would favour this: 6.1:1, clears AA +

+
+ Beamline temperature near limit +
+

APCA Lc ≈ 49, reads as strained

+
+
+

+ What we ship: 2.8:1, fails AA on the ratio alone +

+
+ Beamline temperature near limit +
+

APCA Lc ≈ 59, reads as legible

+
+
+
+
+
+
+

How to read this

+
    +
  • + WCAG 2.2's ratio is polarity-blind: it rates black-on-orange higher + than white-on-orange here, even though white is the better choice. +
  • +
  • + APCA accounts for polarity and light/dark differently, and lines up + with what actually reads well. +
  • +
  • + This is why we design with APCA first, then check WCAG 2.2 as the + compliance floor, not the other way round. +
  • +
+
+ +
+ +### An open trade-off: obvious, readable, compliant + +Choosing a status colour means balancing three things that don't always pull +the same direction: + + + +Pushing hard on readability and compliance can quietly erode "obvious": the +more you lighten a saturated colour to raise its contrast, the closer it gets +to just being pale, and at some point it stops reading as that colour at all. +This isn't hypothetical: it's an open question in DiamondDS today, not a +solved one: + +
+
+
+

Light: Danger text on surface

+
+
+ --ds-danger: #ae1e13 +
+
+
+

+ Shipped today: 7.0:1, clears WCAG +

+ Connection to detector lost: retry required +

+ APCA Lc ≈ 83, still just under where APCA would like + body-sized text +

+
+
+

+ Pushed further for compliance: 9.9:1 +

+ Connection to detector lost: retry required +

+ APCA Lc ≈ 91, and #8a0000 still reads plainly as + danger red: a deep brick red, not a different colour. + Darkening barely costs "obvious" anything here. +

+
+
+
+
+
+

Dark: Danger text on surface

+
+
+ --ds-danger: #ff9088 +
+
+
+

+ Shipped today: 8.1:1, comfortably clears WCAG +

+ Connection to detector lost: retry required +

+ APCA Lc ≈ 58, below where APCA would like body-sized text, but + still unmistakably read as danger red +

+
+
+

+ Pushed further for compliance: 14.5:1 +

+ Connection to detector lost: retry required +

+ APCA Lc ≈ 92, but #ffe2e0 is only a few RGB points + off white (255, 226, 224). Say it plainly: this no longer + reads as red at all, let alone danger. Obvious has been + traded away to get there. +

+
+
+
+
+
+
+

Where this stands

+
    +
  • WCAG alone would call both of today's colours done. Neither fully is: APCA still flags them for regular-sized text.
  • +
  • + The trade-off isn't symmetric. In light mode, closing the gap means + darkening towards a deeper red, which barely touches "obvious." In + dark mode, closing the same gap means lightening towards white, + which erases it. +
  • +
  • + We're weighing a smaller, more careful adjustment, most likely in + dark mode where the gap actually costs something, against + documenting this as a known limitation for now. Either way, this is + a live example of the balance, not a finished answer. +
  • +
+
+ +
+ +## Practical guidance + + + +## Common pitfalls + + + +## When WCAG and APCA disagree + + + +## Future: WCAG 3 and APCA + +WCAG 3 is still evolving, but perceptual contrast models such as APCA are +influencing the direction of accessibility guidance. + +Our approach reflects that direction while staying grounded in current +compliance requirements: + + + +Learn more: + + + +
diff --git a/src/storybook/accessibility/02-cognitive-a11y.mdx b/src/storybook/accessibility/02-cognitive-a11y.mdx new file mode 100644 index 00000000..60bbc7fc --- /dev/null +++ b/src/storybook/accessibility/02-cognitive-a11y.mdx @@ -0,0 +1,119 @@ +import { Meta } from "@storybook/blocks"; + + + +
+ +# Cognitive accessibility and usability + +Accessibility is not only about vision, hearing, or motor input. People also +differ in how they process information, maintain focus, recover from errors, +and work under pressure or fatigue. + +In scientific environments, these differences matter. Users may work for long +periods, switch between multiple tools and data sources, or operate systems +during live experiments where mistakes can be costly. + +This page focuses on cognitive accessibility and practical usability within +complex scientific software. + +## Designing for cognitive accessibility + +Good accessibility and usability often come from the same decisions: +interfaces that are clear, predictable, consistent, and forgiving. + +The goal is not to simplify scientific work itself, but to reduce unnecessary +cognitive load created by the interface. + +## Design objectives + +### Help users understand what things are + + + +### Reduce memory burden + + + +### Help users maintain focus + + + +### Help users avoid and recover from errors + + + +### Design for long-running workflows + + + +## Applying this in Storybook + +When reviewing components and patterns, ask: + + + +## Further reading + + + +