Skip to content
megapromotingLet's talk

Expertise · UI/UX Design

A design system where contrast is calculated, motion has a stop button, and what does not pass the check is written plainly.

We work with colour tokens with a calculated contrast ratio, keyboard patterns checked element by element and animations that stop according to system preference. We verify with a tool, on real pages, and we also publish what failed.

Already builtThe system runs on this site and can be inspected. The token `--muted: #666b65` achieves between 4.72:1 and 5.45:1 on the site’s seven backgrounds — recalculated by us with the luminance formula from WCAG, not taken from a tool. Eight colours written directly in the stylesheet produced 2.88–4.49:1; we found them with axe-core 4.10.2 run on ten live pages and replaced them with the token, and the correction is already in the production-served stylesheet. The second verifiable implementation: the motion-stop mechanism — 38 elements animated infinitely on the homepage, all covered by a pause button, plus the global rule that cancels animations at `prefers-reduced-motion: reduce`.

Most “design systems” are a palette and a list of components. Ours starts with something more boring and more useful: every text colour has a calculated contrast ratio against every background it actually appears on. Not “looks good”, but a number. The secondary text token on this site, `--muted: #666b65`, achieves between 4.72:1 and 5.45:1 across the seven backgrounds used. The required threshold for small text is 4.5:1.

That number did not come from common sense. We ran axe-core 4.10.2 on ten pages of the website and the only WCAG rule systematically breached was contrast: 31 to 43 nodes per page, of which 31 came from the header and footer, so they multiplied across all 147 URLs. Eight colours written directly in the stylesheet produced 2.88:1 to 4.49:1. The worst one, at 10 px and 3.72:1, was exactly the row with the legal links — the path someone takes when they want to exercise a right.

The second part of the system is motion, treated as a function, not as decoration. On the homepage there are 38 elements animated indefinitely. All are covered by a pause button: when pressed, they switch to the stopped state, the label changes, and the state is announced to screen readers. Separately, a global rule cancels any animation and transition when the system requests reduced motion, stops smooth scrolling and even hides the pause button itself, which becomes pointless. We also checked the classic trap — an animation that starts from near-zero opacity and leaves the content invisible when it is cancelled: it does not exist here.

The rest is discipline: a visible 2 px focus indicator, applied only to keyboard navigation; arrow-key traversal in tab groups, following the recommended pattern; forms with errors announced as alerts and confirmations announced as status. And one rule we keep tightly: what we could not verify is written as unverified. The internal audit has a section on tool limitations, and two defects found there are still open — they are below, by name.

What it covers

The work, by component

Colours are tokens, and contrast is a calculated number

The palette sits in a block of 14 variables, and the secondary text uses a single token: `--muted: #666b65`. The ratios, recalculated with the relative luminance formula from WCAG on the site’s seven backgrounds: 5.45:1 on white, 5.15, 5.11, 5.03, 5.02, 4.79 and 4.72. The main text, `--ink: #181a19`, achieves 17.49:1 on white; the green accent `#355c46`, 7.58:1. When a colour does not reach the threshold, the token changes, not the argument in the meeting.

The components have keyboard patterns, not just appearance

The tabs group on the home page uses a roving tab index: only the active tab is reachable with Tab, the others are taken out of the order, and the left/right, Home and End arrows move focus, with the default key behaviour cancelled. It is the pattern from the recommended practices for accessible components, implemented, not quoted. In the site’s base components there are 21 accessible labels, 7 pressed/unpressed states, 3 links between a heading and a region, 2 selection states, 2 regions that announce changes and 2 links between a command and the panel it controls.

Motion has a stop button and turns off at the system preference

On the home page there are 38 automatically started infinite animations lasting more than five seconds — exactly the situation covered by the “Pause, Stop, Hide” criterion. All 38 are covered by pause buttons: when pressed, the animations switch to the stopped state and the button state changes. Separately, at `prefers-reduced-motion: reduce` a global rule cancels any animation and transition, stops smooth scrolling, removes hover movement and hides the pause button. In the main stylesheet there are seven such blocks, plus one in the base stylesheet.

Visible focus indicator, only where needed

A 2 px solid outline, in the accent green, with 5 px offset, applied via `:focus-visible` — so it appears during keyboard navigation and not on mouse click. Checked in the browser, element by element, on the header and on the first content controls: four different elements, the same outline. Nowhere in the stylesheet is there a removed outline without a replacement.

Forms that announce their states

In the site’s contact form, labels are implicit on fields, the error is marked as an alert and the confirmation as a status — so screen readers announce them without the user having to search. The bot trap is correctly hidden: parent without display, marked as hidden for assistive technologies and removed from the tab order. Our own audit confirms that the labelling rules, the name rules for selectors and the autocomplete rules all pass.

The check is done with a tool, on real pages

We run axe-core 4.10.2 on the live page, not in a test environment, on a sample of ten pages covering different patterns: the home page, the catalogue, a product page, the services index, a service page, the solutions matrix, a combination from the matrix, contact, pricing, an article and a legal document. The raw result is written on the page, with the number of nodes, not summarised as “compliant”.

What fails is published, not rounded away

From the same audit: at 320 px width, the command in the header and the menu button overflow the screen by 22 px and the page scrolls horizontally — the arithmetic is 362 px of content in a 280 px container. In the legal documents, links in the text are distinguishable from the surrounding text only by colour, and the ratio between the accent and body text is 1.01:1, so underlining is mandatory. Both are open, with a written fix. We publish them because a buyer who asks for accessibility deserves to see what a real defect list looks like.

No dark theme — stated, not simulated

The site does not have a dark theme. The token block is declared for three selectors that receive identical values, and in the code there is no rule for the system colour-scheme preference. Checked in the browser with a simulated dark scheme: the page stays white. The useful consequence: all measured contrast ratios are valid in both schemes. The unpleasant consequence: the theme switch is dead code from the colour point of view. A real dark theme means fully re-measuring contrast, not copying values.

Typography with rules, not preferences

One family for headings and text, with letter-spacing adjusted by level — tighter on the large headings, `-0.055em` at levels one and two, `-0.035em` at level three — and weight 500 on headings. Paragraphs use line balancing, so one word is not left on the last line. The container has a maximum width of 1240 px with 48 px margins, and buttons have the same line height throughout. These are rules written in the stylesheet, so they apply after we leave.

What it looks like

The path, step by step.

01

The inventory of tasks and bottlenecks

The first deliverable is the list of things people come to do, in order of frequency, with the place where they get stuck today. Without it, any new screen is an aesthetic preference. With it, every later decision has a criterion.

02

Tokens and contrast checking, before screens

The colours, the backgrounds on which they actually appear, and the calculated ratio for each pair are established. We deliver the table of pairs with their ratios. It is much cheaper than finding out at the end that the chosen palette cannot be used for small text.

03

Components, with the keyboard pattern written together with the layout

Each interactive component gets, in the same commit, layout and behaviour: what happens on Tab, on arrow keys, on Escape, what is announced and how. The keyboard pattern added later is a rewrite, not an addition.

04

The tool-based audit, on real pages, with the raw result delivered

We run the tool on a sample that covers different patterns and deliver the raw output, not a summary. Each finding gets the place in the code, the violated criterion, the proposed fix and the verification criterion after the fix — for example: the number of contrast nodes must drop to zero on the page that contains only the header and the footer.

05

Handover, with the list of what was not verified

We hand over the tokens, components, the audit document and the list of what could not be verified, with the reason. On this site, the list includes keyboard activation and emulation of the reduced motion preference in the browser — both tool limits, both written as such.

Token de culoare curaport calculat1component with patternkeyboard typedtogether with the appearance2check with toolpe pagini reale, curezultatul brutpublicat3What fails at step three returns to step one.
A cycle in 3 steps

The data

What we touch, where it lives, and how long it stays

The questions anyone with a data protection officer would ask — asked here before they do.

What we collect in the research stage
The tasks people actually have to do, the context in which they do them, and the point where they get stuck. When this involves recordings, interviews or observing real sessions, it is agreed in advance who participates, what is recorded, who has access and how long it is kept — in writing, not by tacit understanding.
Accessibility audit results
The tool's raw output, grouping of nodes by the colour–background pair, independently recalculated ratios and the code location of each colour. It is delivered as a versioned document alongside the code, not as a presentation — so anyone can reproduce the measurement with the commands in the annex.
What goes into the code and what remains in discussion
The tokens, components, keyboard patterns and motion rules go into the stylesheet and into components. Preferences that cannot be expressed as a rule — “it should look warmer” — remain decisions to be discussed, they do not slip in as values written directly into components. That is where, usually, the colours that fail the check come from.
Visitor preferences
The reduced motion preference is read from the system, via the media query, and is not stored by us. There is no dark theme to remember. Traffic measurement passes through the tool configured for the project and is activated after consent; it does not depend on the design system, but it does belong on the same page.
What we do not assume
We do not assume that an automatic result means compliance. The tool catches part of the criteria; the rest require manual checking, and some things could not be checked at all with the tools available — keyboard activation, for example, because the browser panel used does not produce default activation. Write it like this in the audit: unverified, not passed.

A case

Eight colours, one variable, and the list that remained open

The situation

Own site, recently rebuilt, with an existing token system. Nobody had complained about anything. The question was not “does it look good?”, but “what does a tool with no opinions say?”.

What we built

We injected axe-core 4.10.2 into the live page and ran it on ten pages chosen for different patterns. The only WCAG rule systematically violated was contrast. We grouped the nodes by text–background pair, recalculated each ratio with the relative luminance formula from the standard — so we would not depend on a single tool — and looked for each colour in the style sheet. Eight values written directly in code produced all the permanent failures: 3.89:1 on the 25 footer links, 2.88:1 on the step numbers, 3.72:1 on the row with the legal links, and five more between 3.99 and 4.49. The right token already existed; the problem was that eight rules were bypassing it.

What came out

The eight values were replaced with the token, and the token was adjusted so it passed the threshold on all seven backgrounds of the site. Checked twice: none of the eight colours still appears in the repository, and the style sheet served by production today contains only the token. Recalculated independently: 4.72:1 in the worst case and 5.45:1 on white. The rest of the audit confirmed what was already correct — the focus indicator, the keyboard pattern in the tab groups, the labels and states in the form, full coverage of animations with pause buttons.

What the case does not say

Two findings remained open and are written as such: at 320 px width the header exceeds the screen by 22 px, and in the legal documents the links in the text are distinguished only by colour, at a ratio of 1.01:1 versus the surrounding text — colour alone is not enough; underlining is needed. And there is the method limit: an automatic tool covers part of the criteria. Keyboard activation could not be tested with the tools available and is marked unverified, not passed.

Questions

What people ask us before they call

What does “accessible design” mean for you, concretely?

It means numbers, not adjectives. The secondary text on this site uses a single token, `#666b65`, which gets between 4.72:1 and 5.45:1 on the seven backgrounds where it appears — the required threshold for small text is 4.5:1. The focus is a 2 px outline that appears only when navigating with the keyboard. Motion has a stop button and is completely disabled with the system reduced-motion preference. Each of these statements has a place in the code and a way to verify it.

How do you check it? With what tool?

axe-core 4.10.2, run on the live page, on ten pages covering different patterns — the home page, the catalogue, a product page, the index and a service page, the solutions matrix and one combination from it, contact, prices, an article, a legal document. The raw result, with the number of nodes per page, is included in the document. On top of that we manually recalculate contrast ratios with the formula from the standard, so we do not depend on a single tool. And we explicitly write down what could not be verified.

What failed on your site?

At the time of the audit, the only rule systematically violated was contrast: 31 to 43 nodes per page, of which 31 from the header and footer, multiplied across all 147 addresses. Eight colours written directly in the style sheet produced 2.88 to 4.49:1; the worst, at 10 px and 3.72:1, was the row with the legal links. All eight are now replaced with the token, and the correction is in the sheet served by production. Two things remain open: at 320 px the header exceeds the screen by 22 px, and in the legal documents the links in the text are distinguished only by colour, at a ratio of 1.01:1 versus the surrounding text. Both have remediation written.

Why do you publish defects on your own site?

Because otherwise “accessible” is a word without cost. Anyone can write that they comply with the standard; few show the list of non-conformities, with the number of nodes and the line in the style sheet. If we work together, you will get the same type of list for your site — including the parts that do not reflect well on us. It is cheaper to find out from us.

Does the site have a dark theme?

No, and I say that because it is verifiable: the token block is declared for three selectors that receive identical values, and in the code there is no rule for the colour scheme preference. Emulating the dark scheme in the browser, the page stays white. The good part is that all measured contrast ratios are valid in both schemes. The part to solve is that a theme switch that does not change colours is dead code. A real dark theme means a full re-measurement of contrast — it is not transferred from the light one.

Do animations bother users?

It can, and that is why it has two exits. First: every infinitely animated element is covered by a pause button — on the home page I checked that all 38 are covered, zero uncovered. Second: at the system reduced motion preference, a global rule cancels any animation and transition, stops smooth scrolling, and even hides the pause button. I also checked the classic trap: if an animation starts from near-zero opacity and is cancelled, the content can remain invisible. That does not happen here, because the elements do not have reduced base opacity.

Do you deliver code too, or only mock-ups?

We deliver the tokens and the components in code. A design system that stays in the design tool erodes at the first urgent case: someone writes a colour directly into the component because “it looks better here”, and after six months you have eight parallel colours that fail the check. That is exactly what happened to us too, it is documented above, and that is why the token is the only source of truth for the secondary text colour.

What cannot you verify?

A few concrete things, written as such in the audit. Keyboard activation: the browser panel used does not produce the default activation — pressing Enter on a button does not generate a click — so we do not report conclusions about Enter and Space. Emulation of the reduced motion preference: we could not activate it in the browser, so the rule was checked in source, and a manual test with the preference enabled remains recommended. And perceived load metrics were not available in the tool, so they are marked as unmeasured, not estimated.

Do you guarantee WCAG compliance?

No, and no honest person should claim that based on an automated tool. A tool covers part of the criteria; the rest require manual verification, and some require real users. What we guarantee is the method: declared sample, declared tool and version, raw result published, ratios recalculated independently, remediation with a verification criterion, and the explicit list of what could not be verified. A certificate without that list says nothing.

What the statements above rest on (15 sources)
  1. The eight colours written directly in the style sheet (2.88–4.49:1) no longer exist either in the repository or in the production-served style sheet; in their place is `var(--muted)`https://www.megapromoting.com/_next/static/chunks/0~95cl1qc44v9.css · 2026-09-06

14 of them are code and files from our repositories. We don't publish their name or line: together, on a single page, they would describe too precisely how systems that aren't only ours are built. We go through them with you, in the repository, on request — verification stays possible, it just happens in a conversation.

What would you want to work better?

Tell us about your process. Together we decide what is worth building, what we can connect, and how we check the result.

Let's talk