UX Strategy
UI audit: check your interface against your own design system
How to run a UI audit: inventory your components, compare them with your design system, and turn every inconsistency into a fix someone can make.
A UI audit checks the interface itself: the buttons, fields, cards, type, colours and spacing your live site shows, in every state, against the design system you meant to ship.
You list what exists, compare each item with the system, and write every difference as a rule that names the element and the value it should have. The rule tells someone what to fix, and it can run again to show the difference is gone.
A UX audit of the whole site covers tasks, journeys and accessibility. This post covers the consistency check inside it.
UI audit, UX audit and design system audit
- A UI audit asks whether the shipped interface follows your design system, consistently, in every state.
- A UX audit asks whether people can do what they came for. It includes a UI audit, and much more.
- A design system audit reviews the system itself: whether its tokens, components and documentation are complete and agree with each other. The term also covers checking the shipped site against the system, value by value: see a design system audit of your live site.
The idea is old. In his 1994 summary of inspection methods, Jakob Nielsen listed a “consistency inspection”, in which designers from other projects inspect an interface “to see whether it does things in the same way” as their own. Consistency and standards is also one of his ten usability heuristics; our post on running a heuristic evaluation covers all ten.
Decide what you compare against
A design system usually lives in several places at once:
- Design tokens: the named values for colour, type, spacing, radius and shadow. The W3C Design Tokens Community Group published the first stable version of its token format, 2025.10, in October 2025. It is a Community Group report, not a W3C Standard.
- Component code: the library your templates are built from.
- Design files: the components and styles your designers draw with.
- Written guidelines: when to use which component, and how.
These can disagree. Decide before you start which one wins when they do. Usually it is the tokens and the component code, because that is what ships.
If you have no design system, the audit is where one starts. Brad Frost proposed the interface inventory in 2013 as “a comprehensive collection of the bits and pieces that make up your interface”, and as the groundwork for a design system. Step 1 below builds one.
Step 1: inventory the interface
- By template. Take one representative page of each template, plus any page whose content differs from the rest of its template. Our post on template-based auditing covers picking the page that stands for a template.
- At two widths at least. A phone width and a desktop width, because many components change between them.
- In every state. Default, hover, focus, active, selected, disabled, error, loading and empty. Most inconsistencies hide in the states nobody designed.
- Distinct treatments, not instances. Frost’s advice is to capture the different treatments of a component rather than every copy of it: the four kinds of button, not the four hundred buttons.
Group what you find by component: headings, links, buttons, fields, checkboxes and radio buttons, selects, cards, tabs, accordions, dialogs, alerts, badges, tables, icons and navigation.
Read the values from the browser, not from the stylesheet. The Computed panel in the browser’s developer tools, or getComputedStyle() in a script, gives the values the browser applied after every stylesheet and every override. One row per treatment is enough:
component,treatment,template,page,width,state,values seen,system entry,verdictButton,Primary,Product page,/products/example-chair,390,default,"background rgb(11, 87, 208); radius 6px; height 40px",button/primary,near missButton,Primary,Checkout,/checkout,390,default,"background rgb(22, 128, 61); radius 6px; height 48px",button/primary,one-offField,Text,Checkout,/checkout,390,error,"border rgb(217, 48, 37); no message",field/error,gap in the systemCard,Product,Category page,/category/chairs,1280,hover,"radius 12px; shadow none",card/product,near missStep 2: compare each item with the system
Give every row one of four verdicts:
- Matches. It uses a component and variant from the system, with its tokens.
- Near miss. It is almost a token: a 6px radius where the system has 4px and 8px, a blue one step off the action colour, 15px body text. Usually a value someone typed instead of the token.
- One-off. A treatment with no entry in the system, such as a third card style made for one campaign.
- Gap in the system. The site needs something the system does not have, such as an error state for a select. That is a fix to the system, not to the page.
Check each state for accessibility in the same pass, because a system that gets one wrong gets it wrong on every page:
- Text contrast: at least 4.5:1 for normal text and 3:1 for large text, in every state except disabled, which WCAG exempts. Our post on measuring text contrast against WCAG covers the exceptions.
- Non-text contrast (1.4.11): “visual information required to identify user interface components and states”, such as a field’s border or a selected tab’s marker, needs 3:1 against the colours next to it. Inactive components, and controls whose look the browser sets and the author has not changed, are exempt.
- Focus visible (2.4.7): every control that takes keyboard focus shows it. A reset stylesheet that removes outlines, with no other focus style in their place, fails it everywhere at once.
- Colour alone (1.4.1): an error state shown only by a red border fails; add a message or an icon.
- Target size (2.5.8): controls at least 24 by 24 CSS pixels, or, if smaller, spaced so that a 24px circle centred on each overlaps no other target and no other undersized target’s circle, unless another exception applies.
- Consistent identification (3.2.4): “components that have the same functionality within a set of web pages are identified consistently.” A search button labelled “Search” on one template and “Find” on another fails it.
Step 3: write deviations as rules
“The buttons are inconsistent” is an opinion nobody can close. A rule names where, what and the allowed value, and a browser can check it. Some examples:
- Primary buttons in three blues. Rule:
.button--primaryhas the action colour as its background. - Body text at 15px on articles. Rule:
.article-body pis at least 16px. - Card corners of 6, 8 and 12px. Rule:
.cardhas an 8px radius. - Header icon buttons at 32px. Rule:
.site-header .icon-buttonis at least 44px tall, your system’s size. WCAG’s Level AA minimum (2.5.8) is 24px; 44px is its AAA figure (2.5.5). - Error fields marked only in red. Rule: a field with
aria-invalid="true"has a message tied to it witharia-describedby.
The first four are values a browser reports, so a short Playwright test can check them on one page of each template, at both widths. It assumes a baseURL in your Playwright config:
import { expect, test } from "@playwright/test";type Rule = | { name: string; selector: string; property: string; allowed: string[] } | { name: string; selector: string; property: string; minPx: number };// One rule per deviation the audit found. Colours are written the way// the browser reports them: rgb() for a colour set in hex.const RULES: Rule[] = [ { name: "Primary buttons use the action colour", selector: ".button--primary", property: "background-color", allowed: ["rgb(11, 87, 208)"] }, { name: "Article text is at least 16px", selector: ".article-body p", property: "font-size", minPx: 16 }, { name: "Cards use the 8px radius", selector: ".card", property: "border-radius", allowed: ["8px"] }, { name: "Header icon buttons are at least 44px tall", selector: ".site-header .icon-button", property: "height", minPx: 44 },];// One page for each template.const PAGES = ["/", "/category/chairs", "/products/example-chair", "/checkout"];for (const path of PAGES) { for (const width of [390, 1280]) { test(`UI rules on ${path} at ${width}px`, async ({ page }) => { await page.setViewportSize({ width, height: 900 }); await page.goto(path); for (const rule of RULES) { const values = await page.locator(rule.selector).evaluateAll( (elements, property) => elements.map((element) => getComputedStyle(element).getPropertyValue(property)), rule.property, ); for (const value of values) { const passes = "minPx" in rule ? parseFloat(value) >= rule.minPx : rule.allowed.includes(value); expect.soft(passes, `${rule.name}: found ${value}`).toBe(true); } } }); }}Four things to know before you rely on it:
- Write colours the way the browser reports them. Browsers follow CSS Color 4: a colour set in hex,
rgb(),hsl()or a colour name comes back asrgb(), orrgba()if it is translucent, whileoklch(),lab()andcolor()stay in their own form, with numbers in place of percentages. Copy the allowed values from whatgetComputedStyle()returns, or every element fails. - A selector that matches nothing passes. Where a template must have the component, also check that the selector finds it.
- States need an action first. To check a focus or hover style, focus or hover the element, and wait for any transition to finish, before you read its values.
- Height is the CSS height, not the rendered size. With
box-sizing: content-boxit leaves out padding and border, and a non-replaced inline element, such as a link or aspan, reports the height it was given, orauto. For a size rule that must match what people tap, measure withlocator.boundingBox().
Some rules stay with a person: whether an icon means the same thing everywhere, whether a one-off is justified, and whether a new component belongs in the system.
Step 4: fix at the source
- Fix the token or the component, not the page. A near miss becomes the token it was meant to be. A one-off joins the system or goes.
- Write each finding so someone can act on it. The rule, the template and page, the width and state, the value found, the value expected, a screenshot, and who owns the fix: the design system team or the product team.
- Rank before you fix. A primary button that fails contrast across the site comes before a badge on one page. Our guide to the whole audit explains how to rank findings by severity and by how many visitors meet them.
A UI audit covers how the interface looks and behaves, not every check a template needs. Run it beside our UX checks for each page template.
Step 5: re-check and keep the rules
After the fixes, run the rules again on the same pages, at the same widths and in the same states. Close a finding when the re-run no longer finds it, not when someone says it is fixed. Then keep the rule file in your test suite, so the same drift cannot come back unnoticed. Take a new inventory when the system changes a lot, or before a redesign.
UI review: catch drift before it ships
A UI review checks interface changes in a pull request, before they reach the live site. It keeps the next audit short:
- Look at every state. Review each changed component in its states, at a phone width and a desktop width, in a preview deployment or in your component stories.
- Compare screenshots. A visual comparison against the last approved screenshots shows what changed; a person decides whether the change was meant.
- Run the rules. Run the rule file from step 3 against the preview, so a typed-in colour fails before merge.
- Question new values. A new hex code or pixel value in a stylesheet diff is a question for the reviewer: which token should it be?
The same pipeline can run lint, component and end-to-end accessibility checks in CI.
Frequently asked questions
What is a UI audit?
A review of the interface your live site shows, component by component and state by state, against your design system. Each difference is written down as a rule naming the element and the value it should have, so it can be fixed and checked again.
What is the difference between a UI audit and a UX audit?
A UI audit asks whether the interface follows your design system consistently. A UX audit asks whether people can do what they came for, and covers tasks, journeys, accessibility and performance as well as the interface.
What should a UI audit check?
Every distinct treatment of each component, in every state and at a phone and a desktop width: its colours, type, spacing and radius against your tokens, text and non-text contrast, a visible focus indicator, target size, and whether components with the same function are identified the same way.
Can a UI audit be automated?
The rules can. A rule that names a selector and an allowed value or a minimum size can be checked by a browser test on one page of each template. Whether a one-off is justified, whether an icon means the same thing everywhere and what belongs in the system still need a person.
How often should you run a UI audit?
Take a full inventory when the design system changes a lot or before a redesign. Run the rules from the last audit on every change, so drift is caught before it ships.