Vantage Circle · Accessibility & usability

Making an enterprise product
usable, not just consistent.

The gap between what an enterprise product is bought for and what it is like to use every day

A design system settles what things look like. It says nothing about whether a keyboard can reach them, whether a screen reader can name them, or whether a thumb can hit them. Accessibility isn’t a checklist bolted on at the end; it is a signal that the system isn’t doing enough of the thinking for the people using it. This is the other half: a heuristic audit, an exhaustive component run, and the fixes that came out of both, almost none of which had ever been filed as a bug. Fix the screen once, then fix the thing that kept producing it.

BriefSelf-set, off the back of a marketplace scan: find out why the people who use the product daily rate it far below the people who buy it
RoleDesign Team Manager; ran the audit, set the severity rubric, made the calls, and shipped the fixes in the codebase
MethodNielsen heuristic audit scored on severity 1–4 and effort S/M/L · one exhaustive component run · fixes shipped behind the guardrails from the design-system project
ScopeFour highest-traffic clusters (Recognition, Redemption, Feed, Perks Home) plus the mobile and tablet builds
OutcomeKeyboard reachability, assistive-tech semantics, contrast, touch targets and heading structure fixed across web, mobile and tablet. The measurable part comes later: these ship in waves, and the end-user rating is the number to watch.

01The gap nobody was looking at

Every internal signal said the product was doing fine. The review sites said so too, until you noticed who was writing the reviews.

Enterprise software is bought by one group and used by another. An HR team chooses a recognition platform, configures it, and reviews it on G2 or Capterra. The 3.2M employees who receive recognition through it never touch a review site, except one. So I scanned ten marketplaces and sorted them by who was actually rating.

Fig. 1 Who was rating it, and what they said marketplace scan · captured 22 May 2026

Buyer-facing marketplaces

4.7 / 5

Ease of use, from 5,122+ reviews. G2, GetApp and Software Advice all agree.

Audience: HR admins and buyers. They configure the programme.

The one end-user rating

2.7 / 5

546 ratings on the US App Store, the only marketplace scoring the people who open the product.

Audience: employees. They receive the programme.

Ten marketplaces were scanned. Every high score comes from an audience that buys the product; the lowest comes from the only audience that uses it daily. The admin reviewer pool is also self-selecting: people who chose the tool, reviewing the tool they chose. That split is the whole brief.

Nothing in that gap is proof of an accessibility problem on its own. Marketplace ratings are noisy, and some of the low reviews had nothing to do with usability. But it reframed the question from “is the product good?” to “good for whom?”, and that is a question an audit can answer.

02An audit that could be argued with

“The app feels clunky” is not a finding. It cannot be assigned, estimated, or disproved.

So the audit ran against Nielsen’s ten heuristics, with each checkpoint written as a concrete platform behaviour rather than a principle: not visibility of system status but the points balance stays visible from catalogue through cart to checkout, not just in the catalogue header. Every checkpoint named the component file it applied to, so a finding arrived pre-located.

Each finding got two scores. Severity 1–4, anchored in real examples so the scale could not drift: a 4 means the user cannot complete the task or their points are at risk; a 1 means it reads as unfinished. Effort S/M/L, anchored the same way, in days. Severity against effort produces a shortlist instead of a wish list.

I scoped it to four clusters (Recognition, Redemption, Feed and Perks Home) for a commercial reason: they carry both the demo path and the daily-driver path. The screens a prospect is shown are the screens an employee lives in. Checkout, profile and account settings were explicitly ruled out of this round and marked for a later one.

03Contrast, when the brand colour is an input

A multi-tenant product cannot promise contrast the way a single-brand product can. Every customer sets their own brand colour, and some of them pick badly.

That is an accessibility constraint disguised as a theming feature, and it has to be solved in the token layer rather than per screen. Four rules carry it, all of them decided during the design-system work and all of them load-bearing here:

Status colours are never tenant-derived. A customer whose brand is red does not get red success states. Green stays green.

Text and surface tokens come from the neutral ramp, not the brand ramp, so legibility does not move when the brand does. If contrast depended on the tenant, it would be untestable; there is no single palette to test.

The type scale has a floor. 11px for anything a user has to read. The 8px badge token exists, and is documented as decoration only, never text.

The tiers that sit on photography stay neutral. Icon-only and overlay buttons ignore the tenant colour, because a brand tint over an image is a contrast lottery.

One audit finding came straight from this. A brand card rendered white text over a gradient, which no contrast tool can evaluate and which produced a different ratio per tenant. Swapping it to a solid fill did not just fix one card; it made the card testable, which is the part that lasts.

04One component, exhaustively

“Make this component better” is the brief that produces unfalsifiable design work. So I built a repeatable process and pointed it at the profile widget.

Ideate exhaustively, score every idea against one rubric, then four gates: the two guardrails from the design-system project, an ROI cut, and a stakeholder eyeball where surviving edits sit committed but unpushed until a human sees them running.

174 ideas ideated and judged. Five shipped. Two raised as global proposals. 167 dropped.

Fig. 2 174 ideas, five shipped one component · one exhaustive run
  • 5 shipped
  • 2 raised as global proposals
  • 167 dropped
Every dot is one rated, argued idea. The largest single drop reason was duplication: roughly seventy were the same idea arriving twice, or folded into a stronger sibling. A gate that passes everything is not a gate. Hover a lit dot to read it.

Four of the five are invisible. There is no before-and-after screenshot, because the rendered card is pixel-identical; the commits changed attributes, not appearance. That is the finding, not a gap in the evidence: this is work that only exists for people navigating by keyboard or screen reader, which is why none of it had ever been filed. The people filing bugs were using a mouse.

Fig. 3 The five, and what changed in each one fix per commit, on the component template only
IdeaThe defect, and the changeROI
idea-128Nominate row was mouse-only, a click handler with no keyboard path
+ role="link"  + tabindex="0"  + aria-label
125
idea-104rol="link", a typo, so the role was never applied at all
− rol="link"  − redundant aria-label
118.75
idea-22Four decorative dash images announced to screen readers
alt="dash" → alt="" + aria-hidden="true"
108
idea-20Greetings tile and Get Recognized row fired on click but not on Enter or Space
+ tabindex="0"  + role="button"  + keydown
96
idea-75A nested <a> inside another <a>: invalid HTML, undefined behaviour
inner anchor removed, label moved outward
95.3
No SCSS touched, no tokens consumed, no .ts file opened; the scope guardrail held for all five. ROI is impact-over-effort on the run’s shared rubric; an invisible keyboard fix scores enormously well on it, which is the bias described below.

The rubric had a bias, and finding it changed the workflow

The run surfaced almost entirely accessibility fixes, which looked like a great result until I saw why. An invisible keyboard-operability fix scores enormously well on impact-over-effort; visual craft rarely clears 80. Rank purely on ROI and accessibility wins every run forever, and the visual work never happens. So the workflow gained a focus parameter, and the same 174 ideas produced completely different winners depending on it. A scoring rubric doesn’t remove judgement from design work. It relocates it into the choice of what you are scoring for.

The gate rejected real work, which is how you know it was a gate. Of five visual-pass edits, three were reverted after review and logged as refusals, so nobody re-proposes them in six months.

05The fixes nobody had filed

The profile widget was one component. The audit and the mobile build produced the rest, across three surfaces.

Fig. 4 The rest of it audit-driven fixes across web, mobile and tablet
Keyboard operability (WCAG 2.1.1)
back-to-topScroll control was mouse-only. Made keyboard-operable, given a visible focus ring and a 44×44px minimum target.#19
consent checkboxNot reachable or labelled for assistive tech. Made keyboard-operable and aria-labelled, and switched to a real opt-in, unchecked by default, with Accept gated on it.#11
Assistive-tech semantics (WCAG 1.1.1, 4.1.2)
header logoLink had no accessible name; the logo image was announced as content. Gave the link a name, marked the image decorative.#17
perks & redeemNo page <h1> anywhere, and card titles competing with section titles. Rebuilt the heading hierarchy: page h1, section h2, card h3.mobile
truncated labelsCategory tiles, gift-card names and offer titles clipped with no way to read the full string. Added [title] full text.#14
share dialogClear control announced the literal word “close” as its label; replaced with a proper icon control. Share disabled until a colleague is selected.#12
Contrast and colour
brand cardsWhite count text sat on a gradient, which contrast tools cannot evaluate and which varied by tenant. Swapped to a solid brand fill holding a consistent, machine-readable ratio.#6
focus ringSix different browser-default focus artifacts across dropdowns, chips and inputs. Replaced with one --focus-ring token at 2px; halo opacity 0.25 → 0.10.canon
Touch and pointer
hamburgerTap target below the comfortable minimum on phones. Raised to 48px.mobile
department filterFilter chips under-sized for touch on tablet. Re-spaced to real targets.tablet
offer tilesFilled “Get Offer” hover state stuck on after tap and after dialog close, because it was written for a mouse. Scoped to hover-capable pointers.#13
colleague pickerCard-level hover lift implied the whole card was clickable when only the “+” was. Scoped the feedback to the element that actually responds.canon
Structure and layout
redeem gridsA dead Bootstrap 3 class meant gift-card tiles never stacked full-width on phones. Corrected across redeem, WFH and perks tile grids.mobile
smart bannerOverflowed the viewport horizontally on phones. Constrained and re-scaled.mobile
bottom navSat under the home indicator on notched devices. Added the safe-area inset.mobile
empty carouselsRecommended-offers rendered an empty shell when the array was empty. Module now hides.mobile
Numbered items come from the heuristic audit’s issue log; canon items are design-system rules that apply everywhere; mobile and tablet came out of making the app work at those widths at all. Not one of these had been filed as a bug.

Two of these deserve their own note

The consent checkbox was not really an accessibility fix. It was reachable-by-keyboard work that turned into something else: the box had been pre-checked by default and the Accept action did not depend on it. Making it keyboard-operable and correctly labelled meant admitting that the control had been decorative. It is now unchecked by default, and Accept is gated on it. An audit that only looks for missing aria attributes never finds that.

Removing a focus ring is usually a defect. Several commits here strip focus outlines, which deserves an explanation rather than a footnote. What was stripped were browser-default artifacts (blue bracket marks inside dropdown panels, focus rails on category chips), six inconsistent presentations of the same state. What replaced them is a single --focus-ring token applied everywhere, plus a visible ring added to controls that had none. Focus indication went up, not down; it just stopped being six different accidents.

06Mobile made it structural

The codebase was never built for phones. Client demand changed that, and making the app work at 400px turned a set of polish items into architecture.

Some of it was ordinary responsive work: a banner overflowing the viewport, a dead Bootstrap 3 grid class that stopped gift-card tiles ever stacking full-width, a bottom navigation sitting under the home indicator on notched devices. But the interesting failures were the ones where a desktop assumption had been encoded as an accessibility decision.

The clearest was the offer tile. Its filled state was written as a :hover rule, which on a touch device sticks after the tap and stays stuck after a dialog closes, so the tile lies about its state. The fix is to scope hover feedback to hover-capable pointers, which is a one-line media query and an admission that hover was never a state, it was a mouse.

The same applied to heading structure. Perks and redeem had no page <h1> at all, survivable when everyone is looking at a 1440px layout and navigating by eye, disqualifying when someone is navigating by headings on a screen reader. Rebuilding the hierarchy was not a mobile fix. Mobile is just where it became impossible to ignore.

07What this half is worth

The design-system work made the product consistent. None of it made the product usable, and the two are not the same claim.

A token file cannot tell you that the Nominate row has a click handler and no tabindex. A colour ramp cannot tell you that white-on-gradient is untestable. A component library cannot tell you that a pre-checked consent box is a design decision somebody made. Consistency is a property of the code; usability is a property of the encounter between the product and a person, and only one of those can be linted.

What the foundation did buy was speed. Every fix here landed behind the same guardrails, consumed the same tokens, and needed no new values; five of them touched no SCSS at all. Consistency made the usability work cheap enough to do exhaustively.

The honest limit: these ship in waves, and the numbers that would settle it (the end-user rating, task completion on a phone) come after the sequence completes. What I can show is the gap I found, the method I used, and the fixes that are live. The rest is a measurement I have not taken yet.