Methodology
How a page becomes a score
Which checks run, what we changed from the published taxonomy they come from and why, how the score adds up, and what it can't tell you.
What the score means
The score is a deterministic summary of the design checks we could evaluate on one public snapshot of a site. It is not a prediction of taste, and it is not a ranking of designers. Every point traces back to a check with a written threshold, a measured value and the elements that produced it, so you can verify any result by hand.
A high score means nothing we measure slipped. It doesn't mean the design is finished: judgement calls such as brand fit, imagery and tone aren't measured yet.
How a scan works
- Each scan runs in its own short-lived, isolated virtual machine with a 90-second limit. It never runs on our own servers or network.
- It loads the address you enter and up to two more pages on the same site, at 1440 and 390 px wide, and at 320 px for checks about reflow.
- It records layout boxes, computed styles, the accessibility tree, focus order and element crops. Accessibility rules run through the open-source axe-core engine (4.11.0).
- It only reads. It honours robots.txt, never logs in, fills in forms or presses buttons, and identifies itself as
is-aesthetic (+https://is-aesthetic.com/bot). See the scanner page. - If a page's required stylesheets or fonts fail to load, the render isn't faithful, so the affected checks are excluded rather than scored.
- Reports keep crops of flagged elements (at most 600 px) and one small thumbnail, never full-page copies.
From rubric to checks
The checks come from a published design taxonomy (see attribution) that describes good web design as 533 runnable rules across six domains and 85 principles. We vetted every rule before using any of it: is it valid, does it produce false positives on real sites, does it overlap another rule, and can a public page supply what it needs?
- Rules in the source taxonomy533
Written for judging finished designs, with a method and thresholds each.
- Kept or fixed by vetting423
208 kept as written, 215 kept with a fix. 102 were merged into another rule and 8 dropped.
- Implemented in our engine68
The rest are written up but not yet built. Model-judged rules come later, after validation.
- Scored on public pages today30
Deterministic, runnable on any public page, and, where vetting asked for a fix, with that fix in place.
Of the implemented rules we don't score: 18 were merged into another rule, 12 wait for their vetted fix, 5 need inputs a public page can't give (a brief, a spec, a login or a declared design system), 2 would have to press controls on your site, and 1 was dropped.
The checks we run
Titles, summaries and fixes are our own words. Thresholds are the measured bands from the source rules. Open a check for its thresholds, what we changed and why, and its sources.
Essential 28
C006.1Supporting text steps backVisual communicationMeasured in the browserChanged
Captions, hints and footnotes should look quieter than the text they support: smaller, lighter or muted.
- Passes at
- Supporting text has a quieter cue and no stronger visual cue
- Warning at
- One or two supporting items have no clear hierarchy cue
- Fails at
- A supporting item looks stronger, or at least three have no hierarchy cue
- Widths
- 1440, 390, 320 px
- What we changed
- Exclude error messages (role=alert, aria-errormessage targets, elements styled with the page's error color), status text and tooltips from 'supporting text'. For figcaption and small with no heading, the primary is the adjacent body paragraph.
- Why
- aria-describedby also points at error messages, which should be promoted: GOV.UK's bold red error message is referenced by aria-describedby and would score a promoting cue (Error).
- Threshold
- A starting value we chose, to be calibrated against designer ratings.
- Sources
- Butterick, Headings
C013.2Boxes hold whole groupsVisual communicationMeasured in the browserChanged
A bordered or shaded box should hold a complete group (a list, a nav, a fieldset), not part of one.
- Widths
- 1440, 390 px
- What we changed
- Skip boxes that are a child (or a descendant of a child) of the group. Count a split only when the box is not inside the group and holds 2+ but not all of the group's children.
- Why
- Any card grid built as ul > li with a background per li (most product sites).
- Sources
- Palmer 1992, Common region
C014.1Repeated parts look the sameVisual communicationMeasured in the browserChanged
Cards, buttons and list rows of one kind should share padding, radius, borders, shadow and type.
- Passes at
- Matching component variants differ by at most 1 px
- Warning at
- A measured spacing or size difference is between 2 and 4 px
- Fails at
- A difference exceeds 4 px, or a compared style attribute differs
- Widths
- 1440, 390 px
- What we changed
- Compare only non-content properties (padding, radius, border width and style, shadow, font tuple, and height for single-line controls) within clusters of one variant. Exclude width and multi-line height.
- Why
- Buttons whose widths differ by label length, or masonry cards with content-driven heights: >4 px, Error on any site. Primary and secondary buttons share a role, so 'style differs' (Error).
- Sources
- Nielsen, heuristic 4
C014.5No near-miss variantsVisual communicationMeasured in the browserChanged
A button or card that is almost, but not quite, like its siblings reads as a mistake.
- Widths
- 1440, 390 px
- What we changed
- Count an orphan only when it differs slightly from the main cluster (radius within 4 px, color dE 2-10, padding within 4 px): near-miss drift. Exclude the page's primary CTA, aria-current items, and elements with a distinct label or badge.
- Why
- linear.app's nav shows 'Sign up' as a button among text links; pricing pages highlight one plan card ('Most popular'); a single red 'Delete' button.
- Threshold
- A starting value we chose, to be calibrated against designer ratings.
- Sources
- Nielsen, heuristic 4
C017.2Indents mean nestingVisual communicationMeasured in the browserChanged
Indented text should be nested in the markup, and nested items should be indented on screen.
- Widths
- 1440, 390 px
- What we changed
- Apply only to left-aligned text blocks. Exempt blockquote, figure, pre, aside and callouts. Limit the 'flush nested child' test to semantic nesting (nested lists, tree items, threaded replies).
- Why
- A centered hero (linear.app) whose paragraph is narrower than the h1 is 'inset' from it; any blockquote is an indent without nesting; any div child flush with its parent div is a 'nested child rendered flush'.
- Threshold
- A starting value we chose, to be calibrated against designer ratings.
C019.1Nothing overlapsVisual communicationMeasured in the browserChanged
Text and controls must not cover each other at desktop or phone widths.
- Passes at
- No confirmed occlusion of painted text or a visible control
- Fails at
- A painted box covers text or a control by at least 4 px on both axes and 10% of the smaller painted box
- Widths
- 1440, 390, 320 px
- What we changed
- Exclude ancestor/descendant pairs. Use text Range rects for text, not element boxes; tolerance 2 px. Count an overlap only when one box hides another's text or a control's hit area (elementFromPoint at the covered center). Exempt adornments inside their own control.
- Why
- Password-reveal and clear buttons inside inputs, badge counts on icons, avatar stacks: deliberate overlaps of interactive boxes. A button and its own label text overlap unless ancestor pairs are excluded.
- Sources
- WCAG 2.2 SC 1.4.10
C019.2Dense items have roomVisual communicationMeasured in the browserChanged
Neighbouring list items, cards and grid cells need at least 8 px between their contents, or a visible separator.
- Widths
- 1440, 390 px
- What we changed
- Measure the gap between the content (text line boxes, images, controls) of adjacent items, not between their boxes.
- Why
- Every borderless table and every padded menu or list with 0 margin: edge gap 0 px, Error.
- Threshold
- A starting value we chose, to be calibrated against designer ratings.
- Sources
- Wertheimer 1923, proximity
C031.1Links don't rely on colourVisual communicationAccessibility engineKept as written
Links inside running text need an underline or another cue besides colour.
- Passes at
- Body links have a non-color cue, or 3:1 color contrast plus a state cue
- Fails at
- A body link cannot be identified without its color
- Widths
- 1440, 390 px
- Sources
- WCAG 2.2 SC 1.4.1
C060.1Dates aren't staleFindabilityMeasured in the browserChanged
A past date next to words like "upcoming" or "register by" tells visitors the page is out of date.
- Passes at
- No expired date is described as an upcoming opportunity
- Fails at
- An expired date is still paired with upcoming-event or registration wording
- Widths
- 1440, 390 px
- What we changed
- Pass on past dates without forward-looking wording. Error only for a past date in a block with forward-looking wording (upcoming, register by, coming soon, next, join us on, deadline). Drop 'now', 'open' and 'available' from the list.
- Why
- stripe.com/blog, any changelog or press page: past dates everywhere (Warning). A 2023 changelog entry saying 'Now available' errors.
- Threshold
- A starting value we chose, to be calibrated against designer ratings.
C073.2Headings are distinctFindabilityMeasured in the browserChanged
Sibling headings at the same level shouldn't repeat the same words.
- Passes at
- No heading label is reused for different content within its compared section
- Warning at
- One or two heading-label groups describe conflicting content
- Fails at
- At least three heading-label groups describe conflicting content
- Widths
- 1440, 390 px
- What we changed
- Count duplicates only among sibling headings of the same level under the same parent heading.
- Why
- stripe.com/docs/api repeats 'Parameters' and 'Returns' under every method heading: 3+ strings, Error.
- Sources
- WCAG 2.2 SC 2.4.6
C074.1Paragraphs stay shortFindabilityMeasured in the browserChanged
Paragraphs longer than six sentences are hard to scan on product and task pages.
- Passes at
- A task-page paragraph has at most six sentences
- Warning at
- A task-page paragraph exceeds six sentences
- Widths
- 1440, 390 px
- What we changed
- Score the share of paragraphs over 6 sentences, Warning only, on task and transactional pages; na on long-form editorial (declared in the brief). Use a sentence segmenter that handles abbreviations.
- Why
- Most Wikipedia articles and long-form essays have 7+ sentence paragraphs (Error).
- Sources
- Style Manual, Paragraphs
C077.1Current page is markedFindabilityMeasured in the browserChanged
The nav item for the page you're on should carry aria-current, once per navigation group.
- Passes at
- Each relevant navigation group identifies one current item
- Warning at
- No current marker, but a distinct current item is visible
- Fails at
- A relevant group has conflicting current markers or no identifiable current item
- Widths
- 1440, 390 px
- What we changed
- na when no nav item matches the current URL or section. Missing aria-current with a visual marker is a Warning. Canonical current-location rule; absorb C015.4.
- Why
- Homepages whose nav has no home item (stripe.com, linear.app): 'none in any group', Error.
- Sources
- WCAG 2.2, SC 2.4.8 Location
C078.3Same link text, same placeFindabilityMeasured in the browserChanged
Links with the same text should lead to the same address.
- Passes at
- No identical link label points to conflicting destinations
- Warning at
- One or two conflicting link-label groups
- Fails at
- At least three conflicting link-label groups
- Widths
- 1440, 390 px
- What we changed
- Drop the 'different names, one href' clause (cross-page label consistency is C094.1). Canonical same-name-different-target rule; absorb C076.2 and C095.1.
- Why
- Logo 'Stripe' and nav 'Home' both go to '/'; 'Pricing' and 'See pricing' both go to /pricing: normal on every marketing page (Error at 3+).
C108.1Controls are labelled on screenInteractionMeasured in the browserChanged
Fields need visible labels, and icon-only buttons should be few and use familiar icons.
- Passes at
- Fields are visibly identified and standalone icon controls are clear
- Warning at
- A nonstandard standalone icon control needs a label
- Fails at
- A field has no visible label after the documented search-field exceptions
- Widths
- 1440, 390 px
- What we changed
- Exempt search inputs with an adjacent search button or magnifier, brand or social logo links, and toolbar icons with a tooltip and accessible name. The icon-only count becomes a Warning; 'input lacks a visible label' stays Error.
- Why
- stripe.com and linear.app footers carry 4-6 icon-only social links; app toolbars (Linear, Gmail) have dozens of icon buttons with tooltips: 6+, Error. Docs search fields with placeholder only error.
- Threshold
- A starting value we chose, to be calibrated against designer ratings.
- Sources
- WCAG 2.2, SC 3.3.2
C139.1Images have alt textInclusionAccessibility engineKept as written
Every image needs alt text, or an empty alt if it is decoration.
- Passes at
- Informative images provide a usable text alternative
- Fails at
- An informative image is missing its required text alternative
- Widths
- 1440, 390 px
- Sources
- SC 1.1.1 Non-text Content
C141.2Hidden things stay hiddenInclusionAccessibility engineKept as written
Content hidden from screen readers must not contain links or buttons that still take focus.
- Passes at
- Hidden and presentational elements do not conflict with focus or accessibility semantics
- Fails at
- Hidden content remains focusable, or presentation semantics conflict with an accessible role or name
- Widths
- 1440, 390 px
- Sources
- SC 1.1.1 Non-text Content
C154.2Tab order follows the pageInclusionMeasured in the browserChanged
Pressing Tab should move through the page in reading order, without jumping back up.
- Passes at
- The observed keyboard sequence has no unexplained jumps
- Warning at
- One or two keyboard-order jumps
- Fails at
- At least three keyboard-order jumps
- Widths
- 1440, 390 px
- What we changed
- Define reading order as row-major inside grids and rows and column-major inside multi-column text, and count backward moves against that order. Absorb C168.3.
- Why
- Any row-major card grid: Tab goes card 1, 2, 3, then card 4 in column 1 of the next row, which counts as 'returning to an earlier column' (Error at 3+ rows).
- Threshold
- A starting value we chose, to be calibrated against designer ratings.
C158Targets are big enoughInclusionAccessibility engineKept as written
Buttons and links need a 24 × 24 px hit area, or enough space around them.
- Passes at
- Targets meet the 24 px size or spacing requirement, or an exception
- Fails at
- A target fails both the size and spacing conditions without an exception
- Widths
- 1440, 390 px
C160.1Every control has a nameInclusionAccessibility engineKept as written
Buttons, links and fields need a name that a screen reader can announce.
- Passes at
- Every tested control has a nonempty accessible name
- Fails at
- A tested control has an empty accessible name
- Widths
- 1440, 390 px
- Sources
- WCAG 2.2 SC 4.1.2
C160.2Names include the visible labelInclusionMeasured in the browserKept as written
A control's accessible name should contain the words people see on it.
- Passes at
- A control’s accessible name contains its visible label
- Fails at
- A control’s accessible name omits its visible label
- Widths
- 1440, 390 px
C167.1Headings use heading tagsInclusionMeasured in the browserChanged
Text styled as a section heading should be marked up as one, h1 to h6.
- Passes at
- Section-heading appearance is supported by heading semantics
- Warning at
- A possible heading needs semantic review
- Fails at
- A clear section heading lacks heading semantics
- Widths
- 1440, 390 px
- What we changed
- Error only for blocks >= 1.2x body size followed by 2+ paragraphs (section-like). Card titles, names and prices are a Warning.
- Why
- Bold card titles, testimonial author names, pricing figures and stat labels on any marketing page (Error at 1+).
- Threshold
- A starting value we chose, to be calibrated against designer ratings.
- Sources
- WCAG 2.2 SC 1.3.1
C167.2Heading levels are in orderInclusionAccessibility engineChanged
A page needs one h1, headings that don't skip levels, and a main landmark.
- Passes at
- No heading-outline findings
- Warning at
- One or more heading-outline findings; advisory in the fixed variant
- Widths
- 1440, 390 px
- What we changed
- Warning only.
- Why
- axe 'region' fires once per element outside landmarks; a cookie banner and an unwrapped footer alone give 3+ (Error).
C167.5Data tables use table tagsInclusionMeasured in the browserChanged
Rows of aligned values under a header row should be a real table.
- Passes at
- Detected data grids provide table and header semantics
- Fails at
- A detected data grid lacks table or header semantics
- Widths
- 1440, 390 px
- What we changed
- Require record-like rows (cells holding numbers or short values, aligned across rows). Exclude navigation landmarks, link lists and card grids.
- Why
- Every multi-column footer (bold column titles over aligned links) is a '2x2 data grid without table markup' (Error).
- Sources
- WCAG 2.2 SC 1.3.1
C168.1Code order matches the screenInclusionMeasured in the browserChanged
Blocks should come in the same order in the markup as they appear on screen.
- Passes at
- Visual and DOM reading order agree within content regions
- Warning at
- One or two reordered blocks outside sequential content
- Fails at
- Order differs within instructions, or at least three blocks are reordered in one region
- Widths
- 1440, 390 px
- What we changed
- Derive the visual reading order with a column-aware segmentation (recursive XY-cut) and count inversions only within one column or region.
- Why
- A page with a sidebar, or a two-column feature section (text left, captioned image right): many 'reordered' blocks (Error at 3+).
- Sources
- WCAG 2.2 SC 1.3.2
C170.2Live regions are scopedInclusionAccessibility engineKept as written
Live regions must be valid and small, never the whole page.
- Passes at
- Live regions use valid values and a bounded scope
- Fails at
- A live-region attribute is invalid, the region is page-level, or its area exceeds half the viewport
- Widths
- 1440, 390 px
- Threshold
- A starting value we chose, to be calibrated against designer ratings.
C173Works at 320 px wideInclusionMeasured in the browserChanged
At 320 px the page mustn't scroll sideways, and nothing from the desktop page may go missing.
- Passes at
- At 320 px, non-exempt content fits and desktop content remains reachable
- Fails at
- Non-exempt content overflows or desktop content cannot be reached at 320 px
- Widths
- 1440, 390, 320 px
- What we changed
- Count content as present when it is reachable by opening menus or disclosures at 320 px. Ignore display:none responsive duplicates whose twin is visible.
- Why
- Every responsive site: nav links collapse into a hamburger menu at 320 px, so 'controls from 1440 px' are missing (Error).
- Sources
- WCAG 2.2 SC 1.4.10
C194.1Crops keep the subjectExpressionMeasured in the browserChanged
Images cropped to fill their frame should still show most of the picture.
- Passes at
- Every measured image retains at least 60% visible area
- Warning at
- An image retains less than 60% visible area; advisory in the fixed variant
- Widths
- 1440, 390, 320 px
- What we changed
- Use as an unscored prefilter that selects images for C194.3, or Warning only. Use the selected picture/srcset source's ratio.
- Why
- A 16:9 hero photo cropped to a 9:16 mobile box keeps 32% (Error), even when object-position keeps the subject centered.
- Threshold
- A starting value we chose, to be calibrated against designer ratings.
CAR-14.1Notices can be dismissedTransparencyMeasured in the browserChanged
Informational banners and toasts need a close button.
- Passes at
- Every noncritical notification can be dismissed
- Fails at
- A noncritical notification has no dismissal control
- Widths
- 1440, 390 px
- What we changed
- Limit to notification components (toast, banner, inline notification classes or roles), not every polite live region.
- Why
- Live result counts ('12 results') and loading text in aria-live=polite regions are 'noncritical notifications without a close button' (Error), on any site.
- Sources
- IBM Carbon, Notifications
Recommended 2
C112.1Required fields are markedInteractionMeasured in the browserChanged
A form should show which fields are required before people start filling it in.
- Passes at
- Required and optional fields can be distinguished within the form
- Fails at
- A mixed form leaves a required field unidentified
- Widths
- 1440, 390 px
- Applies to
- Pages with forms
- What we changed
- Pass forms where every field is required and none is marked (GOV.UK convention). C167.4 checks the markup; this rule checks the visible indication.
- Why
- Login (email and password) and newsletter (email) forms with all fields required and nothing marked: Error, though GOV.UK's convention passes them.
- Sources
- W3C, Understanding SC 3.3.2
C112.2Format rules are shownInteractionMeasured in the browserChanged
Fields with rules people can't guess (a pattern, a length, a password rule) need visible hint text.
- Passes at
- Controls with non-obvious constraints have a persistent hint
- Fails at
- A non-obvious constraint has no persistent explanatory hint
- Widths
- 1440, 390 px
- Applies to
- Pages with forms
- What we changed
- Require hints only for constraints a user could not guess: pattern, minlength, min/max ranges, password-creation rules (not login), accept, and dates typed as text. Exclude type=email, login passwords, native date and time pickers, and maxlength >= 100. Absorb C109.3.
- Why
- Every login form (email and password inputs) and newsletter signup (email): no hint, Error.
- Sources
- W3C, Understanding SC 3.3.2
Bonus 0
No bonus checks are scored yet. Craft checks join this tier once they pass validation.
Scoring
Each check counts once per report, using its worst outcome across the pages scanned: fail, then warning, then pass. A check excluded on one page doesn't erase a result measured on another, and a check excluded everywhere never enters the score.
80
Essential points, shared equally by the essential checks that applied.
20
Recommended points, shared by the recommended checks that applied. If a page has no surface for them, all 100 points go to essential, and the other way round.
+5
Bonus at most: 0.5 for each bonus pass and 0.25 for each warning. Bonus checks never lower a score.
A pass earns a check's full share, a warning half, a failure nothing. The score is the base plus the bonus, capped at 100 and rounded to one decimal. With ten essential checks applying, for example, each is worth 8 points; failing two of them and warning on one gives 80 − 16 − 4 = 60, plus any recommended points.
| Grade | Score |
|---|---|
| A | 90 or more |
| B | 80 to 89.9 |
| C | 70 to 79.9 |
| D | 60 to 69.9 |
| F | below 60 |
Domain scores use the same formula, restricted to the checks in each of the six domains. A domain with nothing measured has no score. A report with no evaluated essential or recommended check has no score and no grade, which is different from a zero.
Exclusions
An excluded check never counts as a failure. A check is excluded when:
- the page has nothing for it to test, such as no form for a form check;
- it couldn't be measured reliably, for example because required stylesheets failed to load;
- it needs something a public page can't give, such as a design brief or a login;
- robots.txt or the site's response blocked the page.
Every report lists its excluded checks and the reason for each.
Limits
- Text contrast isn't measured yet. The contrast rules are written but not implemented, so low-contrast text doesn't cost points today.
- Judgement calls that no rule can measure, such as brand fit, tone, imagery and originality.
- Anything behind a sign-in, a cookie wall or a click.
- Other screen sizes, zoom levels, dark mode and print.
- Pages beyond the first three on a site, and content that loads only far down a long page.
- Speed, uptime and search ranking.
- Thresholds marked as starting values haven't been calibrated against designer ratings yet, and may move.
Attribution
The checks are adapted from Frontier Design Bench Draft, v0.10, a published Notion page (last edited 2026-10-21 18:32 Pacific). We rewrote each check's title, summary and fix in our own words, applied the changes listed above, and score only the rules described on this page.
Accessibility checks follow the Web Content Accessibility Guidelines (WCAG) 2.2, a W3C Recommendation.
Sources the checks cite
- Allison et al. 2019, JMIR Formative ResearchC060.1
- Butterick, HeadingsC006.1
- IBM Carbon, NotificationsCAR-14.1
- Material Design, Understanding layoutC017.2
- Moshagen & Thielsch 2010, Facets of visual aestheticsC194.1
- Nielsen, heuristic 4C014.1, C014.5
- Palmer 1992, Common regionC013.2
- SC 1.1.1 Non-text ContentC139.1, C141.2
- Style Manual, ParagraphsC074.1
- WCAG 2.2 SC 1.3.1C167.1, C167.5
- WCAG 2.2 SC 1.3.2C168.1
- WCAG 2.2 SC 1.4.1C031.1
- WCAG 2.2 SC 1.4.10C019.1, C173
- WCAG 2.2 SC 2.4.3 Focus OrderC154.2
- WCAG 2.2 SC 2.4.6C073.2
- WCAG 2.2 SC 2.5.8 Target Size (Minimum)C158
- WCAG 2.2 SC 4.1.2C160.1
- WCAG 2.2 Understanding Info and RelationshipsC167.2
- WCAG 2.2 Understanding Label in NameC160.2
- WCAG 2.2 Understanding Status MessagesC170.2
- WCAG 2.2, SC 2.4.4 Link PurposeC078.3
- WCAG 2.2, SC 2.4.8 LocationC077.1
- WCAG 2.2, SC 3.3.2C108.1, C112.1, C112.2
- Wertheimer 1923, proximityC019.2
Versions
- Source taxonomy
- v0.10, read 2026-10-22 00:16 Pacific
- Engine revision
- 5787c9aa1de9
- Rule registry
- sha256 b3beb6ad6a8b
- Accessibility engine
- axe-core 4.11.0
- Checks scored
- 30 (Visual communication 8, Findability 5, Interaction 3, Inclusion 12, Expression 1, Transparency 1)
Every report records the engine revision and registry digest it was scored with. Changing either needs a new validation run.