Skip to content

fix(a11y): bring the reported colour-contrast failures up to AA - #356

Merged
maksimzinchuk merged 1 commit into
mainfrom
fix/VCST-5862-contrast
Sep 4, 2026
Merged

maksimzinchuk merged 1 commit into
mainfrom
fix/VCST-5862-contrast

Conversation

@maksimzinchuk

@maksimzinchuk maksimzinchuk commented Sep 4, 2026 •

Copy link
Copy Markdown
Collaborator

What the 25 violations actually are

Measured with axe on the running Vendor Portal rather than derived from the source. They come from a few elements, repeated — not 25 independent places. That matters, because --neutrals-400 has 46 uses in the framework and a blanket swap would have been wrong.

Element light dark fix after (light / dark)
User role label, 11px 2.41:1 3.41:1 --neutrals-500 4.54 / 5.09
Relative timestamps (×7) 2.52:1 white row · 2.09:1 selected row 3.16:1 · 2.83:1 --neutrals-600 7.81 · 6.49 / 5.76 · 5.16
Sorted column header 4.41:1 passes --primary-800 5.6 / 8.96
Environment banner 2.79:1 fails on primary and danger black label ink 6.33–12.33 / 4.56–8.32

--neutrals-600 rather than -500 for the timestamps because -500 is still 3.94:1 on the selected-row tint.

The palette's own comments already say what these tokens are for: -400 is "muted text / disabled", -500 is "secondary text (WCAG AA ~5:1)". The defect was text reaching for the disabled ink.

Both themes, from one change

The first three are token references, not values, so each theme resolves them against its own scale. The ticket audited Light only; Dark had the same two defects and is fixed by the same swap.

The environment banner needed more than a swap

Its label was white on every variant — 1.70:1 to 3.32:1 in Light, worst on amber, and no shade of amber fixes white text. In Dark the theme-following ink already failed on primary (4.34) and danger (3.15) before any change here.

Sweeping all seven variants in both themes, from the live cascade:

ink worst, light worst, dark
white (before) 1.70 warning 2.52 warning
--additional-50 (dark's value) — 3.15 danger
--neutrals-800 3.19 neutral 2.12 warning
black 6.33 danger 4.56 danger

Black is the only ink that clears AA on every accent background, and no palette token can carry it: every candidate flips with the theme — --additional-950 is #000000 in Light but #ebebeb in Dark — while these backgrounds stay mid-tone in both. Hence a fixed value, with the reasoning in the comment.

The neutral variant, and an unmodified banner which shares its background, keep the theme-following token: that grey is too dark for black (4.43) and the token passes in both (4.74 / 4.72).

No background changed — every variant keeps the colour it was designed with; only the label moved.

Not swapped

--neutrals-400 stays wherever it marks a disabled or inactive control — WCAG 1.4.3 exempts those, and darkening them would make disabled read as enabled.

The toolbar's disabled title was in QA's list and is not fixed here: axe flagged it only because the button exposed no disabled state at all, so axe could not know the exemption applied. #355 adds aria-disabled and the finding disappears — confirmed by elimination, since this PR does not touch that colour.

Verification

Full axe pass over #/, #/products, #/orders and #/offers, both themes:

before   light 23 nodes   dark 16 nodes      (colour-contrast; no other rule fires)
after    light  0         dark  0

The last three failures were app code, not framework — the dashboard widget empty states — fixed in vendor-portal#154. With that and this in, the four routes report zero axe violations of any rule in either theme.

vue-tsc clean · vitest run 4123 passed, exit 0 · lint:check, prettier and stylelint clean.

One limit worth stating: the story-level axe gate has color-contrast disabled as a documented exception, so nothing in CI keeps this from drifting back.

Committed with --no-verify: the pre-commit hook lints only the staged files, and that narrow invocation reports a false import/no-unresolved the full lint:check does not.

Closes VCST-5862

@github-actions

github-actions Bot commented Sep 4, 2026 •

Copy link
Copy Markdown

📦 Preview published for commit 38334f4

Install the preview with dist-tag:

npm install @vc-shell/framework@pr-356

Or pin to the exact commit:

npm install @vc-shell/framework@2.6.0-rc.0-pr356.38334f4

Published packages (dist-tag pr-356, version 2.6.0-rc.0-pr356.38334f4):

  • @vc-shell/framework
  • @vc-shell/api-client-generator
  • @vc-shell/create-vc-app
  • @vc-shell/config-generator
  • @vc-shell/migrate
  • @vc-shell/ts-config
  • @vc-shell/mf-config
  • @vc-shell/mf-host
  • @vc-shell/mf-module
  • @vc-shell/vc-app-skill

Measured with axe on the running Vendor Portal rather than derived from the
source: the violations come from a few elements, repeated, not from 25
independent places.

- User role label: 2.41:1 light, 3.41:1 dark. -400 is the palette's
  muted/disabled ink; -500 is the one it documents as secondary text.
- Relative timestamps: 2.52:1 on a white row and 2.09:1 on the selected-row
  tint; 3.16:1 and 2.83:1 in dark. -600 rather than -500, because -500 is
  still 3.94:1 on that tint.
- Sorted column header: 4.41:1, just under. -800 is the next step on the
  same scale.

Those three are token references, not values, so each theme resolves them
against its own scale and one change fixes both: light clears at 4.54 / 7.81
/ 5.6, dark at 5.09 / 5.76 / 8.96.

The environment banner needed more than a swap. Its label was white on every
variant, measuring 1.70:1 to 3.32:1 in light, and in dark the theme-following
ink already failed on primary and danger. Sweeping all seven variants in both
themes shows black is the only ink that clears AA on every accent background
(4.56:1 at worst, dark danger, up to 12.33:1), and no palette token can carry
it: every candidate flips with the theme while these backgrounds stay
mid-tone. So the six coloured variants take a fixed black, and the neutral
variant — with the unmodified banner, which shares its background — keeps the
theme-following token, which passes there and where black would not.

No background changed. -400 stays wherever it marks a disabled or inactive
control, which WCAG 1.4.3 exempts.
@maksimzinchuk
maksimzinchuk merged commit 477cb52 into main Sep 4, 2026
12 checks passed
@maksimzinchuk
maksimzinchuk deleted the fix/VCST-5862-contrast branch September 4, 2026 12:52
maksimzinchuk added a commit that referenced this pull request Sep 8, 2026
…358)

QA found `.dashboard-stat-item__value--success` at **4.13:1** on
vcmp-dev while verifying VCST-5862, and correctly noted neither #356 nor
#154 touches it.

Checking the other two modifiers found the same defect:

| modifier | light before | dark before | after | after (dark) |
| --- | --- | --- | --- | --- |
| success | **4.13** | 7.54 | `-700` → 6.88 | 9.45 |
| warning | **2.01** | 7.46 | `-800` → 6.22 | 10.60 |
| danger | **3.80** | **4.33** | `-700` → 6.01 | 5.76 |

### Why the shades differ

Each is the **lightest step that clears AA in both themes**, measured
rather than picked for uniformity. `-700` leaves warning at 3.98 in
light, so amber takes a deeper step — that is a property of amber, not
an inconsistency, and the comment in the file says so with the numbers.

### Why only one of the three was reported

The modifier renders only when a widget carries a stat of that kind, so
whether it appears depends on the data in front of you. That is also why
my own "after: 0" run on VCST-5862 did not show even the success case —
the widgets held no success stat at that moment. QA made the same point
about node counts being row-count dependent; the distinct **signature**
is the stable unit, not the node total.

Measured from the live cascade by forcing each modifier, rather than
waiting for data to produce them.

### Verification

`vue-tsc` clean · `vitest run` 4158 passed, exit 0 · `lint:check`,
prettier and stylelint clean. Browser sweep of all three modifiers in
both themes, all six combinations clearing AA.

Committed with `--no-verify`: the pre-commit hook lints only the staged
files, and that narrow invocation reports a false `import/no-unresolved`
the full `lint:check` does not.

Contributes to VCST-5862
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant