is-aesthetic

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?

  1. Rules in the source taxonomy533

    Written for judging finished designs, with a method and thresholds each.

  2. Kept or fixed by vetting423

    208 kept as written, 215 kept with a fix. 102 were merged into another rule and 8 dropped.

  3. Implemented in our engine68

    The rest are written up but not yet built. Model-judged rules come later, after validation.

  4. 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.
  • 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).
  • 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).
  • 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.
  • 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.
  • 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.
  • 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
  • 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.
  • 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).
  • 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.
  • 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.
  • 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
  • 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
  • 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
  • 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.
  • 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).
  • 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+).
  • 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).
  • 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.

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.
  • 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.

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.

Grades
GradeScore
A90 or more
B80 to 89.9
C70 to 79.9
D60 to 69.9
Fbelow 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

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.