demo-app

path D:\dev\ai agents\facts+\examples\demo-appcommit a4050a4files 3generated 2026-06-06T18:52:44.721Zllm off
447 total fail 10 review 424 pass 8 n/a 5 assessed 4%
Critical 1Serious 7Moderate 2Minor 0Info 0
Grade reflects the 18 assessed checkpoint(s) — 4% coverage. 424 remain in review.
F
28
score
Perceivable0
2 fail18 review0 pass
Operable60
1 fail21 review2 pass
Understandable0
2 fail20 review0 pass
Robust
0 fail16 review3 pass
Semantics & structure0
1 fail21 review0 pass
Forms & input0
1 fail22 review0 pass
Visual hierarchy
0 fail22 review0 pass
Typography
0 fail22 review0 pass
Color & contrast
0 fail22 review0 pass
Spacing & layout
0 fail20 review1 pass
Motion0
1 fail19 review0 pass
Responsive50
1 fail23 review1 pass
Performance
0 fail26 review0 pass
Loading & states
0 fail21 review0 pass
UX copy
0 fail22 review0 pass
Internationalization
0 fail22 review0 pass
Navigation & IA
0 fail22 review0 pass
Feedback & affordance
0 fail21 review0 pass
Trust & privacy0
1 fail21 review0 pass
Consistency
0 fail23 review1 pass

Failing — 10

PER-001Every image carries a meaningful text alternativecriticalstatic
1 <img> element(s) have no alt attribute.
Fix. Add alt text describing the image's purpose; use alt="" for purely decorative images.
evidence: index.html:10:5
intent: Screen-reader users perceive images only through their alt text. A missing alt makes the content invisible; an empty alt is the correct signal for decoration.
wcag:1.1.1html:H37
FRM-001Associate a label with every form controlseriousstatic
1 form control(s) with no id, aria-label, aria-labelledby, or title — almost certainly unlabelled.
Fix. Associate a <label for> (or aria-label) with every control so its purpose is announced.
evidence: index.html:16:5
intent: An unlabelled input is unidentifiable by screen-reader and voice-input users; they can't know what data is expected.
wcag:3.3.2html:H44
MOT-001Respect prefers-reduced-motionseriousstatic
CSS motion exists but no prefers-reduced-motion query was found — motion can't be reduced for users who ask for less.
Fix. Add @media (prefers-reduced-motion: reduce) { * { animation: none; transition: none; } } or scoped equivalents.
evidence: styles.css:3:9 styles.css:6:1 styles.css:7:10
intent: Users with vestibular disorders or motion sensitivity can trigger migraines or disorientation from animation; the OS-level reduced-motion preference is their control.
wcag:2.3.3
OPR-002Make custom interactive elements fully keyboard-operableseriousstatic
2 non-interactive element(s) with a click handler but no role + tabindex + key handler.
Fix. Use a <button>/<a>, or add role, tabindex="0", and a keydown handler so keyboard and AT users can activate it.
evidence: ProductList.jsx:6:9 index.html:15:5
intent: Custom widgets built from divs and spans don't inherit keyboard behaviour; users who cannot use a mouse are entirely blocked.
wcag:2.1.1aria:button
PER-004Pre-recorded media provides captionsseriousstatic
1 media element(s) without a <track> for captions/subtitles.
Fix. Provide captions via <track kind="captions"> (and audio descriptions where relevant).
evidence: index.html:17:5
intent: Deaf and hard-of-hearing users rely on captions; video/audio without a <track> excludes them.
wcag:1.2.2
RSP-002Allow users to zoom the viewportseriousstatic
1 viewport meta(s) disable zoom (user-scalable=no / maximum-scale=1) — this blocks low-vision users from zooming.
Fix. Remove user-scalable=no and maximum-scale; let users pinch-zoom.
evidence: index.html:5:3
intent: Disabling pinch-zoom (user-scalable=no, maximum-scale=1) prevents low-vision users from magnifying content they cannot otherwise read.
wcag:1.4.4
TRU-001Add rel=noopener to target=_blank linksseriousstatic
1 target="_blank" link(s) without rel="noopener" — a reverse-tabnabbing risk.
Fix. Add rel="noopener noreferrer" to links that open a new tab.
evidence: index.html:13:5
intent: Links that open a new tab grant the new page access to window.opener, enabling tab-napping attacks where the opener page is replaced with a phishing site.
owasp:A05:2021
UND-001Declare the page's human languageseriousstatic
1 document(s) without <html lang> — screen readers can't pick the right pronunciation/voice.
Fix. Set <html lang="en"> (or the right language) on the document root.
evidence: index.html:2:1
intent: Without lang on <html>, screen readers may apply the wrong voice/pronunciation engine, making content incomprehensible.
wcag:3.1.1html:H57
SEM-001Use exactly one h1 per pagemoderatestatic
1 document(s) with more than one <h1> — there should be exactly one top-level heading per page.
Fix. Keep a single <h1> and structure the rest with <h2>–<h6> in order.
evidence: index.html:12:5
intent: Multiple h1s erode the document outline that screen readers and search engines rely on; a single h1 anchors the page topic.
wcag:1.3.1
UND-002Give every page a descriptive titlemoderatestatic
1 document(s) with a missing or empty <title> — the title names the tab, bookmark, and history entry.
Fix. Add a concise, descriptive <title> to each document.
evidence: index.html:1
intent: The <title> is the first thing screen readers announce and the text shown in browser tabs; a vague or duplicate title disorients users.
wcag:2.4.2html:H25

Needs review — 424

?CLR-003Normal-size body text meets 4.5:1 contrast ratiocriticalnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Darken foreground or lighten background colors until the WCAG AA threshold of 4.5:1 is met; use a tool such as the APCA or WebAIM contrast checker.
intent: Text below 24 px (or 18.67 px bold) requires a minimum 4.5:1 contrast ratio against its background to be readable by users with moderate low vision.
wcag:1.4.3
?I18N-002Translated strings are never built by concatenationcriticalnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Replace concatenated translations with ICU-style named placeholders, e.g. 'Hello, {name}!' as one message key.
intent: Joining translated fragments ('Hello, ' + name) breaks word order for many languages and produces ungrammatical output.
nielsen:H4
?MOT-006Content must not flash more than three times per secondcriticalnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Remove or throttle any blinking, strobing, or rapid-toggle animations to fewer than 3 flashes per second.
intent: Rapidly flashing content can trigger photosensitive seizures in users with photosensitive epilepsy.
wcag:2.3.1
?OPR-005Keyboard focus never becomes trapped in a componentcriticalnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Ensure all widgets allow Tab to exit naturally; only trap focus intentionally inside open modal dialogs.
intent: A focus trap outside a modal dialog prevents keyboard users from leaving a widget, effectively locking them into it.
wcag:2.1.2
?OPR-018Content does not flash more than three times per secondcriticalnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Remove or throttle fast-flashing animations; use the prefers-reduced-motion query as a safety net.
intent: Flashing above the 3 Hz threshold can trigger photosensitive seizures, which are life-threatening.
wcag:2.3.1
?TRU-004Password inputs use type=passwordcriticalnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Set type="password" on all password fields; never use type="text" for passwords.
intent: A password field without type=password is visible in plain text and not treated as a credential by browsers, password managers, or AT.
owasp:A07:2021html:input-password
?TRU-014Secrets and PII are never placed in URLscriticalnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Pass sensitive data in request bodies (POST) or Authorization headers; never in URLs.
intent: Query strings appear in server logs, referrer headers, and browser history; secrets or identifiers there leak to third parties.
owasp:A02:2021
?CLR-004Large text meets 3:1 contrast ratioseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Verify large display and heading text against the 3:1 threshold; increase contrast if below it even if the text is visually large.
intent: Text at or above 24 px regular (or 18.67 px bold) benefits from a relaxed 3:1 threshold because larger letterforms are easier to discern at lower contrast.
wcag:1.4.3
?CLR-005UI components and graphical objects meet 3:1 contrastseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Darken input borders, icon strokes, and chart lines until they meet 3:1 against both the element background and the page background.
intent: Borders, icons, chart lines, and control boundaries that convey meaning must be distinguishable from their surroundings by users with low vision.
wcag:1.4.11
?CLR-006Focus indicators meet 3:1 contrast against adjacent colorsseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Use a 2 px solid outline in a color that achieves 3:1 against the element background and avoid outline: none without a replacement.
intent: Keyboard users need a visible focus ring; a low-contrast or hidden outline makes the focused element indistinguishable, blocking navigation.
wcag:1.4.11wcag:2.4.11
?CLR-007Color is never the sole means of conveying informationseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add a text label, icon, or underline alongside color to communicate status, category, or emphasis so the message survives greyscale.
intent: Approximately 8% of men and 0.5% of women have color-vision deficiency; information communicated only by hue is lost to them.
wcag:1.4.1
?CLR-008Links are distinguishable from surrounding body textseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Preserve the default underline on body-copy links, or verify that link and body text colors differ by at least 3:1 using a contrast tool.
intent: If inline links differ from body text only by color and the contrast between the two colors is below 3:1, color-blind users cannot identify them.
wcag:1.4.1
?CLR-011Error and success states use more than red/green aloneseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Combine color with a text label or icon for all error, warning, and success indicators to ensure the status is understood without color.
intent: Red–green color blindness (deuteranopia/protanopia) is the most common form; error and success states in red/green without a secondary cue exclude those users.
wcag:1.4.1
?CLR-012Contrast ratios hold in dark modeseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Test every text and UI element pair in dark mode with a contrast analyser; don't assume inverting a light palette is sufficient.
intent: A palette that meets contrast in light mode may invert into low-contrast combinations in dark mode if dark tokens are not independently verified.
wcag:1.4.3wcag:1.4.11
?CLR-013Text on images has a scrim or overlay to ensure legibilityseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Place a semi-transparent dark or light overlay behind text-on-image sections, or use text-shadow as a fallback to guarantee legibility.
intent: A photo background has unpredictable luminance; text placed over it without a scrim or solid overlay can fail contrast at any point in the image.
wcag:1.4.3
?CLR-016Data visualization palettes are color-blind safeseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Use a color-blind safe palette (e.g. Okabe–Ito) for all data visualizations and supplement color with direct labels or patterns.
intent: Chart palettes chosen for visual appeal often produce indistinguishable hues for deuteranopic or protanopic users, destroying the information the chart conveys.
wcag:1.4.1
?CLR-019Icon contrast meets 3:1 against backgroundseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Darken icon color tokens until the icon-to-background contrast reaches 3:1; apply the same verification in dark mode.
intent: Informational icons that are too light to see clearly against their background fail low-vision users who rely on them for navigation or status.
wcag:1.4.11
?CLR-020State colors meet their contrast threshold in both light and dark themesseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Maintain separate dark-mode token overrides for each semantic state color and test each combination against the WCAG threshold.
intent: A status color verified only in light mode may collapse below the threshold in dark mode where background luminance is very different.
wcag:1.4.3
?CPY-001Link text describes its destination, not the actionseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Replace 'click here' / 'learn more' with destination-descriptive text, e.g. 'View pricing plans'.
intent: Generic phrases like 'click here' or 'read more' are meaningless out of context. Screen-reader users who navigate by links hear only the link text.
wcag:2.4.4nielsen:H6
?CPY-003Error messages state what went wrong and how to fix itseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Rewrite errors to explain what happened and what the user should do, e.g. 'Password must be at least 8 characters — try again'.
intent: Vague errors like 'Something went wrong' leave users stranded. A good error identifies the problem and offers a concrete next step.
nielsen:H9
?CPY-011Placeholder text illustrates format, not a label substituteseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add a persistent <label> for every field; restrict placeholder to a short format hint that is not the only labelling mechanism.
intent: Using placeholders as the only label disappears on input, leaving users without a reminder of what the field requires.
wcag:1.3.1nielsen:H6
?CPY-014Confirmation dialogs state the consequence, not just a questionseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Rewrite confirmation dialogs to name the exact object being affected and state irreversibility where applicable.
intent: 'Are you sure?' tells the user nothing; 'This will permanently delete 3 files — this cannot be undone' lets them make an informed decision.
nielsen:H1nielsen:H5
?CPY-016Copy avoids string concatenation for translated textseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Replace string concatenation with a message format that keeps the full sentence together using named interpolation.
intent: Hard-coded concatenation ('Hello, ' + name + '!') breaks in languages with different word order and prevents proper translation.
nielsen:H4
?FBK-001System status is always visible to the userseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add persistent status indicators (spinners, badges, banners) so users are never left guessing what the system is doing.
intent: Users need to know the current state of the system at all times. Hiding progress, status, or outcome leaves them confused about whether their action was received.
nielsen:H1
?FBK-002Actions receive visible feedback within one secondseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Show an immediate loading indicator or optimistic state change on every action that may take time.
intent: Responses that take longer than ~1s break the user's flow and feel unresponsive. Immediate acknowledgement preserves the sense of direct manipulation.
nielsen:H1
?FBK-004Keyboard focus is always visibleseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Ensure :focus-visible styles are present and have sufficient contrast; never use outline:none without a replacement.
intent: Keyboard and switch-access users navigate with Tab; without a visible focus ring they cannot tell which element is active.
wcag:2.4.7wcag:2.4.11
?FBK-009Error states are explained and actionableseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Write error messages in plain language explaining what failed and what the user can do next.
intent: Opaque error messages ('Something went wrong') give users no path forward. Clear explanations with next steps reduce abandonment.
nielsen:H5nielsen:H9
?FBK-014Destructive actions require explicit confirmationseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add a confirmation dialog with a clearly-labelled destructive CTA, or provide an undo mechanism within a reasonable window.
intent: Irreversible actions (delete, clear, overwrite) without a confirmation step cause unrecoverable mistakes that erode trust.
nielsen:H5
?FBK-020Dynamic feedback is announced to assistive technologyseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Use role="status" (polite) for non-urgent feedback and role="alert" (assertive) for errors; ensure these are always in the DOM.
intent: Toasts, inline errors, and status changes that update visually are invisible to screen-reader users unless exposed via an ARIA live region.
wcag:4.1.3aria:alert
?FRM-007Required fields are indicated beyond color aloneseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Mark required fields with aria-required="true" and provide a text or symbol indicator explained in a legend.
intent: Red asterisks with no text label fail users who are colour-blind or who use non-visual AT.
wcag:1.4.1wcag:3.3.2
?FRM-008Validation errors are programmatically linked to their fieldseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add aria-describedby="errorId" to the input and id="errorId" to the error message element.
intent: Error text placed near an input visually is not associated with it for AT unless aria-describedby connects them.
wcag:3.3.1aria:aria-describedby
?FRM-009Inline errors are announced to assistive technology on occurrenceseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Render error messages in an aria-live="assertive" container or move focus to the error on submission failure.
intent: Errors that appear silently after blur or submit are never heard by screen-reader users whose focus stays on the offending field.
wcag:3.3.1aria:aria-live
?FRM-011Placeholder text is not used as a substitute for labelsseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add a visible <label> and demote placeholder to a format hint (e.g. "e.g. john@example.com").
intent: Placeholder disappears on input, leaving users with no reminder of what the field expects; it also has insufficient contrast.
wcag:3.3.2html:H65
?FRM-016Radio and checkbox groups are wrapped in fieldset/legendseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Wrap related radios or checkboxes in <fieldset><legend>Question text</legend>…</fieldset>.
intent: Individual radio buttons read in isolation ('Yes / No') lose the question context; fieldset/legend groups them with their shared prompt.
wcag:1.3.1html:H71
?FRM-023Destructive form actions require explicit confirmationseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add a confirmation dialog or a distinct confirm step before any irreversible form action.
intent: A misclick on 'Delete account' or 'Cancel order' with no confirmation causes unrecoverable harm, especially for motor-impaired users.
wcag:3.3.4nielsen:H5
?I18N-001All user-facing strings are externalised into message filesseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Extract all literal UI strings into a locale message file and replace them with translation-function calls (e.g. t('key')).
intent: Hard-coded strings in source code cannot be translated without modifying code, making localisation expensive and error-prone.
nielsen:H4
?I18N-003Plural forms use locale-aware plural rules (ICU or CLDR)seriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Migrate quantity strings to ICU plural syntax and ensure the translation framework applies CLDR plural rules per locale.
intent: English has two plural forms; Arabic has six. Hard-coded '1 item / N items' logic fails in most other languages.
nielsen:H2
?I18N-004Dates, times, and numbers are formatted via Intl APIsseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Replace manual date/number formatting with Intl API calls that accept a locale parameter derived from the user's preference.
intent: Hard-coded formats (MM/DD/YYYY, 1,000.00) are wrong or ambiguous in most non-US locales and cannot be localised without code changes.
nielsen:H2
?I18N-006Layouts support right-to-left (RTL) script directionseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Set dir='rtl' on the <html> element for RTL locales and use CSS logical properties (margin-inline-start, border-inline-end, etc.) throughout.
intent: Languages like Arabic and Hebrew read right-to-left; an LTR-only layout mirrors the reading direction and damages comprehension.
wcag:1.3.4
?I18N-011Accessible names (alt, aria-label) are also translatedseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add all non-visible text strings that convey meaning (alt, aria-label, placeholder, title) to the message catalogue alongside visible strings.
intent: Translating visible text while leaving alt attributes and aria-labels in the source language produces a bilingual screen-reader experience.
wcag:1.1.1wcag:3.1.1
?I18N-012No essential text is baked into raster or vector imagesseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Move text out of raster images into HTML or SVG overlays; use real text styled with CSS.
intent: Text inside image files cannot be extracted by translation tools or screen readers; it must be updated manually for each locale.
wcag:1.4.5
?I18N-020All text content is served and stored as UTF-8seriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Configure servers, databases, and build tools to use UTF-8 throughout, and declare the charset in every HTML document.
intent: Non-UTF-8 encodings corrupt characters in many scripts (CJK, Arabic, Cyrillic) and are rejected by modern tools.
html:H57
?MOT-009Parallax effects provide a reduced-motion alternativeseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Remove parallax transform updates inside the prefers-reduced-motion: reduce media query.
intent: Parallax scrolling involves large-scale motion that triggers vestibular symptoms in sensitive users.
wcag:2.3.3platform:material-motion-accessibility
?MOT-013Avoid large-scale parallax and zoom that trigger motion sicknessseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Replace zoom-parallax effects with subtle opacity or small-scale transforms, and disable entirely under reduced-motion.
intent: Large viewport-scale motion (zoom-based parallax, scroll-jacking) causes nausea in vestibular-sensitive users.
wcag:2.3.3
?MOT-015Auto-advancing carousels can be paused by the userseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add a pause button to any auto-advancing carousel and stop it on hover/focus per WCAG 2.2.2.
intent: Auto-play carousels move content without user intent; users with cognitive or motor disabilities can't keep up.
wcag:2.2.2
?NAV-002A skip-to-main-content link is the first focusable elementseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add a visually-hidden-until-focused skip link as the very first element in the <body>, pointing to id='main-content'.
intent: Keyboard and screen-reader users must tab through the entire navigation on every page without a skip link, which is exhausting on navigation-heavy sites.
wcag:2.4.1
?NAV-008Browser back and forward work correctly with the navigation historyseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Use the History API (pushState / replaceState) or the router's built-in history management for every route change.
intent: SPAs that manipulate state without updating the history stack break the back button — a top user complaint.
nielsen:H3
?NAV-010Mobile navigation is keyboard and touch accessibleseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Implement the ARIA disclosure or dialog pattern for mobile nav menus; ensure all items are reachable and closable without a mouse.
intent: Hamburger menus that are only mouse-operable exclude keyboard-only users and can be difficult for touch users with motor impairments.
wcag:2.1.1aria:disclosure
?NAV-018Focus is managed explicitly on client-side route changesseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Move focus to an h1 or a focus-management div at the top of the incoming route's main content immediately after the route transition completes.
intent: SPA route changes replace content without a page reload; without explicit focus management, screen-reader users are stranded at the old position.
wcag:2.4.3
?NAV-021Mega-menus are keyboard operable and close on Escapeseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Implement mega-menus using the ARIA disclosure or menubar pattern; expose open/close to keyboard via Enter, Space, and Escape handlers.
intent: A mega-menu that can only be opened by hovering excludes keyboard-only users; one that traps focus prevents them from escaping.
wcag:2.1.1aria:menu
?OPR-004Never remove the visible focus indicatorseriousstatic
1 outline suppression(s) found alongside focus styles — verify focus is always clearly visible.
Fix. Instead of outline:none, design a custom :focus-visible style that meets contrast requirements.
evidence: styles.css:2:16
intent: Keyboard users navigate by watching where focus is; outline:none without a custom replacement leaves them disoriented.
wcag:2.4.7
?OPR-006Provide a skip-navigation link to bypass repeated blocksseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add <a class="sr-only focus:not-sr-only" href="#main">Skip to main content</a> as the first DOM element.
intent: Keyboard users must Tab through every header item on every page load without a mechanism to skip to main content.
wcag:2.4.1
?OPR-008Focus is not obscured by sticky headers or overlaysseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Apply scroll-padding-top equal to the sticky header height, or use scroll-margin-top on anchors.
intent: A focused element hidden behind a sticky banner or cookie bar is invisible, making the current keyboard position unknown.
wcag:2.4.11
?OPR-010Multi-pointer gestures have a single-pointer alternativeseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Provide buttons or equivalent controls that replicate any multi-finger gesture.
intent: Pinch-to-zoom or two-finger swipe gestures are unusable for people who operate the device with one pointer.
wcag:2.5.1
?OPR-012Visible labels match the control's accessible nameseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Ensure aria-label / aria-labelledby begins with the exact visible label text or remove the override.
intent: When the accessible name differs from the visible label, voice-control users cannot activate the control by speaking what they see.
wcag:2.5.3
?OPR-014Drag-and-drop operations have a pointer alternativeseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add a keyboard-accessible and single-pointer-accessible move or reorder mechanism alongside drag.
intent: Dragging is difficult or impossible for users with tremors; a drop target with a button-based pick-and-place is required.
wcag:2.5.7
?OPR-016Timeouts are adjustable or warn before expiringseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add an extension dialog that appears 20+ seconds before timeout, or remove the time limit entirely.
intent: Auto-expiring sessions and timed forms cut off users who need more time — including those using screen readers or switch access.
wcag:2.2.1
?OPR-019Focus returns to a logical position after closing a modalseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Store a reference to the opener and call opener.focus() when the dialog closes.
intent: When a dialog closes, focus dropping to the body or top of the page forces keyboard users to re-navigate the entire page.
wcag:2.4.3aria:dialog
?OPR-021Escape key closes overlays, menus, and dialogsseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add a keydown listener for Escape on every overlay and call the close handler with focus restoration.
intent: Keyboard users expect Escape to dismiss overlays; widgets that ignore it trap users who have no other exit.
wcag:2.1.2aria:dialog
?OPR-024Focus visible on all custom-styled form controlsseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Apply a :focus-visible outline to all custom form controls; ensure the style has at least 3:1 contrast against the background.
intent: Replaced-element controls (select, checkbox, radio) with custom CSS often lose their native focus ring, leaving keyboard users without position cues.
wcag:2.4.7wcag:2.4.11
?PER-009Icon-only controls have an accessible nameseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add aria-label (or a sr-only span) to icon-only controls.
intent: A button showing only an icon (✕, ☰) is nameless to AT unless an aria-label or visually-hidden text is supplied.
wcag:1.1.1aria:button
?PER-012Text scales to 200% without loss of contentseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Use relative units and flexible containers so layouts reflow when text grows.
intent: Low-vision users zoom; fixed pixel containers can clip or overlap text when they do.
wcag:1.4.4
?PER-014Content reflows to a single column at 320pxseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Adopt responsive, fluid layouts; avoid fixed widths wider than the viewport.
intent: At a 320px-equivalent width, two-dimensional scrolling forces painful side-to-side reading.
wcag:1.4.10
?PER-020Background images don't carry essential informationseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Move informative imagery into <img> with alt, or add an equivalent text alternative.
intent: CSS background images are invisible to AT; essential content there is simply lost.
wcag:1.1.1
?PRF-005Achieve Largest Contentful Paint under 2.5 sseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Preload the hero image, inline critical CSS, use a CDN, and eliminate render-blocking resources to hit the 2.5 s LCP threshold.
intent: LCP measures when the largest above-fold element becomes visible. Exceeding 2.5 s means users wait noticeably before seeing meaningful content, increasing bounce rate.
cwv:LCP
?PRF-006Achieve Interaction to Next Paint under 200 msseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Break up long tasks, move heavy work off the main thread with Web Workers, and avoid synchronous layout reads in event handlers.
intent: INP captures how quickly the page responds to all user interactions. Values above 200 ms feel sluggish and erode trust in the UI.
cwv:INP
?PRF-012Code-split bundles by routeseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Use React.lazy / dynamic imports at route boundaries to split the bundle; verify with bundle analysis that entry chunks do not include page-specific code.
intent: Shipping every route's code in a single bundle forces users to parse JavaScript they do not need on the current page, delaying interactivity.
cwv:INP
?PRF-019Avoid forced synchronous layout (layout thrashing)seriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Batch DOM reads before writes, or use requestAnimationFrame to separate measurement from mutation; prefer CSS transforms over layout-triggering properties.
intent: Reading layout properties (offsetHeight, getBoundingClientRect) immediately after DOM writes forces the browser to synchronously flush styles, stalling the main thread and worsening INP.
cwv:INP
?PRF-021Virtualize long lists and infinite-scroll viewsseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Replace large mapped lists with a virtualizer component; keep rendered row count proportional to viewport height rather than total data length.
intent: Rendering thousands of DOM nodes at once inflates memory, slows layout, and makes scrolling janky. Windowing renders only the visible slice, keeping the DOM small.
cwv:INP
?PRF-027Avoid blocking the main thread with long tasksseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Profile with Chrome DevTools Performance panel; split long tasks with scheduler.yield or setTimeout(0); move CPU-heavy work to a Web Worker.
intent: Tasks longer than 50 ms block user interactions, directly increasing INP. Long tasks should be broken up or moved off the main thread.
cwv:INP
?ROB-005Custom widgets expose name, role, and value to assistive technologyseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Apply the appropriate ARIA role, aria-label/labelledby, and state attributes (aria-checked, aria-expanded, etc.).
intent: AT cannot describe a custom widget — its purpose, state, or current value — unless the correct ARIA semantics are present.
wcag:4.1.2aria:patterns
?ROB-009aria-labelledby and aria-describedby reference existing idsseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Verify each aria-labelledby/aria-describedby value matches a rendered element's id; use stable IDs.
intent: When an aria-labelledby or aria-describedby id points to a nonexistent element, the association is silently broken and AT reads nothing.
wcag:4.1.2aria:aria-labelledby
?ROB-010Required ARIA parent and child roles are presentseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Wrap owned roles in their required container: listbox > option, tablist > tab, grid > row > gridcell.
intent: Orphaned ARIA roles (option without listbox, row without rowgroup/grid) violate the ownership rules and break AT tree parsing.
aria:required-context
?ROB-011aria-hidden is never applied to focusable contentseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Remove focusable children from aria-hidden subtrees, or use visibility:hidden / display:none instead.
intent: A focusable element inside an aria-hidden subtree is reachable by keyboard but invisible to AT, creating a phantom focus state.
aria:aria-hidden
?ROB-016Widget role matches actual keyboard and interaction behaviourseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Choose the ARIA role whose keyboard conventions the component fully implements; implement them if not.
intent: A role that doesn't match the widget's behaviour (e.g. role=slider on something that isn't a slider) gives AT users wrong instructions.
aria:patternswcag:4.1.2
?ROB-018Interactive elements have an accessible nameseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add visible text, aria-label, or aria-labelledby to every interactive element; never leave them name-less.
intent: An AT user reaching an unnamed button or link hears only 'button' or 'link' with no indication of purpose.
wcag:4.1.2aria:accessible-name
?ROB-019State and property changes are reflected in ARIA attributesseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Bind ARIA state attributes to the same reactive state driving the visual update.
intent: A widget whose visual state changes (expanded, selected, disabled) but whose ARIA attributes are not updated is invisible to AT users.
wcag:4.1.2aria:aria-expanded
?RSP-007Ensure touch targets meet the 24 × 24px minimum (target 44px)seriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Expand small controls with padding to reach at least 24px in each dimension; aim for 44px per Apple/Google HIG.
intent: Controls smaller than 24px are reliably difficult to hit with a finger; WCAG 2.2 requires a minimum of 24 × 24px.
wcag:2.5.8platform:apple-hig-touch-targets
?RSP-008Prevent horizontal scrolling at small viewport widthsseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Set overflow-x: hidden on the root and audit elements that overflow with negative margins or fixed widths.
intent: Two-dimensional scrolling is painful on mobile; all content should reflow into a single scrollable column.
wcag:1.4.10
?RSP-013Do not lock or restrict screen orientationseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Remove any orientation lock (Screen Orientation API, CSS orientation media query restrictions) and test both orientations.
intent: Forcing portrait or landscape prevents users who have their device mounted in a fixed orientation from using the page.
wcag:1.3.4
?RSP-015Avoid hover-only affordances on touch devicesseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Use @media (hover: hover) to restrict hover-only affordances and provide tap-accessible alternatives unconditionally.
intent: Interactions exposed only on :hover (e.g. edit buttons inside cards) are inaccessible on touchscreens.
wcag:2.1.1
?RSP-019Content remains readable without horizontal zoomingseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Ensure minimum font sizes (≥16px for body) and fluid layouts so no horizontal zoom is needed to read content.
intent: If reading the page requires zooming in and panning side to side, the responsive design has failed.
wcag:1.4.4
?SEM-002Pages include all required landmark regionsseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Wrap the site header in <header>, primary navigation in <nav>, main content in <main>, and the footer in <footer>.
intent: Users of screen readers navigate via landmarks; without <header>, <nav>, <main>, and <footer> they must read linearly through every page.
wcag:1.3.1aria:landmark-regions
?SEM-005Use <nav> for site and page navigationseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Replace navigation wrapper <div>s with <nav>; label multiple navs with aria-label.
intent: A list of links wrapped in a <div> is not recognised as navigation; users relying on landmark shortcuts can't jump to it.
wcag:1.3.1aria:navigation
?SEM-007Data tables include th, scope, and captionseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add <th scope="col"> or <th scope="row"> for headers and a descriptive <caption> to each data table.
intent: Without <th> and scope, screen readers can't associate cells with their headers, making tabular data impossible to comprehend.
wcag:1.3.1html:H43
?SEM-009Interactive controls use native button or anchor elementsseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Replace click-handler <div>/<span> controls with <button> or <a> to get keyboard and AT support for free.
intent: Clickable <div>s and <span>s have no native keyboard or AT semantics; users who don't use a mouse can't activate them.
wcag:4.1.2aria:button
?SEM-017Labels are associated with their form controlsseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Use <label for="id"> or wrap the control inside <label> for every form field.
intent: A visible label adjacent to an input but not programmatically linked is invisible to screen readers and voice-input users.
wcag:1.3.1html:H44
?SPC-015Provide adequate edge padding on small screensseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Ensure the root or layout wrapper has padding-inline of at least 1rem on small screens via a responsive rule.
intent: Without horizontal padding on small viewports, text and controls touch the screen edge and become hard to tap or read.
wcag:1.4.10
?SPC-019Avoid overlapping or colliding elementsseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Replace absolute-positioned hacks with proper layout (flex/grid/overflow) to prevent collisions.
intent: Overlapping text or controls obscure each other and can make content unreadable or controls untappable.
wcag:1.4.10
?STA-002Show a visible loading indicator for async operationsseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Introduce a loading boolean or isLoading state and conditionally render a spinner or progress indicator during all async fetches.
intent: Without a loading indicator, users do not know whether their action was registered or the app is frozen, increasing uncertainty and retry clicks.
nielsen:H1
?STA-005Display error states with a recovery actionseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Catch errors at the API/fetch layer and render an error component with a plain-language message and a 'Try again' or equivalent button.
intent: When an operation fails, users need to know what went wrong and how to recover. A bare error message with no action dead-ends the interaction.
nielsen:H9
?STA-008Disable interactive controls while an operation is pendingseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Set disabled={isLoading} on submit buttons and any controls that would trigger conflicting operations; restore them once the request resolves.
intent: Allowing double-submission or concurrent conflicting actions during a pending request leads to race conditions and inconsistent server state.
nielsen:H4
?STA-010Catch errors at component boundaries with error boundariesseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Wrap routes, widgets, and data-heavy sections in ErrorBoundary components that render a friendly fallback with a reload or retry option.
intent: An unhandled rendering error in one component can crash the entire React tree. Error boundaries isolate the failure to the affected subtree.
nielsen:H9
?STA-012Never dead-end the user after a failureseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Audit all error and empty state components; ensure each includes a primary navigation action alongside the error message.
intent: A dead-end page or state — one with no navigation, help, or action — strands the user. Every failure view should offer at least one meaningful exit.
nielsen:H3
?STA-018Require confirmation before destructive actionsseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Show a confirmation dialog for delete, archive, and publish actions; for low-stakes actions an undo toast is sufficient instead of blocking dialog.
intent: Accidental deletions or irreversible operations cause data loss and erode trust. A confirmation step gives users a chance to reconsider.
nielsen:H5
?TRU-002Sanitise any raw-HTML injection sinksseriousstatic
1 raw-HTML injection sink(s) (dangerouslySetInnerHTML / v-html / innerHTML=) — confirm the input is sanitised to avoid XSS.
Fix. Sanitise all dynamic HTML with a library such as DOMPurify before inserting it into the DOM.
evidence: ProductList.jsx:8:12
intent: Setting innerHTML, dangerouslySetInnerHTML, or document.write with untrusted data is the primary vector for stored and reflected XSS.
owasp:A03:2021
?TRU-003All resource loads use HTTPSseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Replace all http:// external URLs with https:// equivalents; use protocol-relative URLs (//…) only when HTTPS is guaranteed.
intent: HTTP resources on an HTTPS page are mixed content: browsers block or warn on them, undermining both security and trust.
owasp:A02:2021
?TRU-007Tracking and analytics require explicit user consentseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Gate tracking script initialisation behind a consent check; do not fire analytics on page load by default.
intent: Loading analytics, advertising, or fingerprinting scripts before consent violates GDPR, PECR, and similar regulations and erodes user trust.
owasp:A05:2021platform:gdpr
?TRU-008Cookie and consent UI avoids dark patternsseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Make 'Reject All' as visually prominent as 'Accept All'; do not pre-select non-essential purposes; allow one-click rejection.
intent: Pre-checked boxes, equal-prominence accept/reject buttons hidden behind layers, or no reject option are deceptive dark patterns that undermine informed consent.
owasp:A05:2021nielsen:H3
?TRU-010Deceptive and dark-pattern UI elements are absentseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Audit checkout and sign-up flows for undisclosed charges, pre-ticked opt-ins, and confusing double-negatives; remove all of them.
intent: Hidden costs, forced continuity, roach motel subscriptions, and misdirection undermine user autonomy and create legal liability.
nielsen:H3
?TRU-012Form submissions go to secure, validated endpointsseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Verify all form action attributes use https:// and that the receiving endpoint validates all inputs server-side.
intent: A form whose action points to an HTTP URL, an unvalidated third party, or a non-existent handler silently discards or exposes user data.
owasp:A03:2021
?TRU-013A Content-Security-Policy header is configuredseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Configure a CSP header; start with Content-Security-Policy: default-src 'self' and progressively add needed origins.
intent: CSP is the most effective mitigation against XSS by whitelisting approved content sources and blocking inline injection.
owasp:A03:2021
?TRU-019Third-party iframes are sandboxed appropriatelyseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add sandbox to third-party iframes and restore only necessary capabilities (allow-scripts, allow-forms, etc.) one by one.
intent: Unrestricted iframes can execute scripts, submit forms, or access top-level navigation; sandbox limits what embedded content can do.
owasp:A05:2021
?TRU-020External scripts use Subresource Integrityseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Generate SHA-384 hashes for all external scripts and stylesheets and add them as integrity attributes with crossorigin="anonymous".
intent: CDN-hosted scripts can be tampered with; SRI ensures the browser refuses to execute a script whose hash doesn't match the expected value.
owasp:A08:2021
?UND-004Receiving focus does not trigger a context changeseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Reserve context changes for explicit user activation (click, Enter, Space); never fire them on focus alone.
intent: Unexpected navigation or form submission on tab-focus disorients screen-reader and keyboard users who are merely exploring.
wcag:3.2.1
?UND-005Changing a setting does not cause a surprise context changeseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Replace auto-submit on change with a submit button; if auto-submit is needed, warn users in advance.
intent: Auto-submitting a form when a select changes removes users' agency and surprises those using AT who didn't intend to submit.
wcag:3.2.2
?UND-009Form errors identify the specific field and describe the issueseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Render per-field error messages linked via aria-describedby; move focus to the first error on submit.
intent: A generic 'form has errors' message forces users to hunt; AT users have no way to navigate to the problem field.
wcag:3.3.1
?UND-010Every input has a persistent visible labelseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add a <label> element for every input; supplement (never replace) labels with placeholder text.
intent: Placeholder text disappears on input and provides no label for AT; users with cognitive impairments forget what a field is for.
wcag:3.3.2html:H44
?UND-012Legal or financial submissions can be reviewed and corrected before finalisingseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add a summary/confirm step before finalising legal or financial transactions.
intent: Irreversible actions submitted by mistake can cause serious real-world harm; users need a chance to check and correct.
wcag:3.3.4
?UND-014Authentication does not rely on cognitive function tests aloneseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Offer email-link or passkey login as an alternative; if CAPTCHA is used, provide an audio/image-based alternative.
intent: Puzzles, memory tasks, or CAPTCHA without alternatives block users with cognitive or visual disabilities.
wcag:3.3.8
?UND-019Destructive or irreversible actions require explicit confirmationseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Show a confirmation dialog naming the item to be deleted; label the confirm button with the specific action.
intent: Accidental deletions or permanent actions without confirmation cause data loss and cannot be undone by the user.
wcag:3.3.4nielsen:H5
?VIS-001Every view exposes exactly one primary actionseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Designate one primary action and style all others as secondary or tertiary variants.
intent: Multiple equally-prominent CTAs force users to decide between options of unclear relative importance, slowing decisions and increasing abandonment.
nielsen:H4
?VIS-008The focal point commands sufficient contrast against its surroundingsseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Increase the luminance or size contrast between the primary focal point and its immediate background.
intent: A primary message or action that blends into the page background fails to attract attention, undermining the intended hierarchy.
nielsen:H4
?VIS-017Competing CTAs are avoided on the same surfaceseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Audit each page for duplicate primary-button styles and downgrade all but the single most important one.
intent: Two equally-weighted primary buttons on one page create the paradox of choice, slowing or preventing user action.
nielsen:H4
?VIS-018Visual order matches interaction priorityseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Reorder the layout so primary content appears above-the-fold and secondary content follows in descending priority.
intent: When the element the user should act on first appears below a less important element, they may never reach it, or reach it disoriented.
nielsen:H4
?VIS-020Above-the-fold content clearly states value and next stepseriousnone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Compress the masthead and move the headline and CTA above the fold; push hero imagery behind or beside the text.
intent: Users decide whether to engage within seconds; if the headline and primary CTA require scrolling, many will not reach them.
nielsen:H4
?CLR-009Placeholder text meets 4.5:1 contrast against the input backgroundmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Darken placeholder text tokens so they meet 4.5:1 contrast; avoid grey-on-white placeholders below #767676.
intent: Placeholder text at low contrast is mistaken for already-entered data and is unreadable for low-vision users.
wcag:1.4.3
?CLR-014Selection highlight contrast is sufficientmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Test the custom ::selection color pair against WCAG thresholds or remove the override to use the browser default.
intent: Custom ::selection colors must meet 3:1 contrast so selected text remains readable when users copy or review highlighted content.
wcag:1.4.3
?CLR-015Hover and active states maintain contrast requirementsmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Verify hover and active state colors with a contrast tool and adjust the hover delta so the ratio stays above the threshold.
intent: Interactive states that darken or lighten a button or link can push contrast below thresholds, making hovered states unreadable.
wcag:1.4.3wcag:1.4.11
?CLR-017Semantic color tokens are applied consistentlymoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Enforce semantic token usage via a linter rule or component API that disallows raw hex values for status colors.
intent: Using the 'danger' token for some errors and a raw red hex for others means contrast is only verified at the token level, not at the point of use.
css:custom-properties
?CLR-021Gradient text retains legibility across the full gradient rangemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Test gradient text at the lightest point of the gradient against the background; if it fails, add a stroke, text-shadow, or switch to solid text.
intent: A gradient applied to text may start accessible at one end but dip below threshold at the midpoint or opposite end, depending on the background.
wcag:1.4.3
?CLR-022Brand colors have documented accessible variants for text usemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Derive an 'on-brand' accessible text token by darkening the brand hue until it meets 4.5:1 on white; use this token wherever the brand color appears as foreground text.
intent: Raw brand colors are chosen for visual identity, not legibility; using them as text foregrounds often produces insufficient contrast, especially on white.
wcag:1.4.3
?CON-003Reuse design-system components instead of one-off implementationsmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Audit the codebase for duplicated UI primitives; replace one-off implementations with the canonical design-system component and delete the duplicate.
intent: Custom one-off components diverge from the design system, creating visual inconsistencies and duplicating maintenance burden for every future design change.
nielsen:H4
?CON-004Use spacing and sizing tokens from a shared scalemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Define a spacing scale (e.g., 4-px base grid) in the design system; replace hardcoded pixel values with the corresponding token (space-4, space-8, etc.).
intent: Arbitrary pixel values for margins and padding produce visually inconsistent layouts that feel misaligned. A shared spacing scale ensures harmonious rhythm.
nielsen:H4
?CON-005Use a single icon set with a consistent stylemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Choose one icon set project-wide; audit all icon imports and replace any icons from other libraries with the canonical set's equivalent.
intent: Mixing icon libraries (e.g., outlined icons from one set and filled icons from another) creates visual noise that signals a lack of design polish.
nielsen:H4
?CON-006Use consistent button variants and sizes across the appmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Standardize on design-system button variants (primary, secondary, ghost, destructive) and enforce through ESLint or a component library lint rule.
intent: Buttons with inconsistent sizes, colors, or border radii make the UI appear unfinished and force users to re-learn affordances on each page.
nielsen:H4
?CON-007Use consistent terminology and labels throughout the UImoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Create a UI copy glossary that defines canonical terms for entities and actions; audit copy for synonyms and standardize on the glossary term.
intent: Calling the same concept by different names on different screens (e.g., 'Delete' vs 'Remove' vs 'Discard') confuses users and increases cognitive load.
nielsen:H4
?CON-008Format dates and numbers consistently across the appmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Create shared formatDate and formatNumber utilities; search for inline date/number formatting and replace all usages with the shared utility.
intent: Seeing 'Jan 5, 2025' on one screen and '05/01/25' on another disrupts trust and can cause genuine misreading of dates in international contexts.
nielsen:H4
?CON-009Apply consistent layout patterns across pagesmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Extract a shared Layout component used by every page; avoid page-specific overrides to the global layout structure.
intent: Pages with differing header positions, sidebar widths, or content widths make the app feel disjointed and increase the learning curve for navigation.
nielsen:H4
?CON-010Use consistent interaction patterns for similar affordancesmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Document and enforce interaction patterns in the design system; audit similar components for divergent behaviors and align them.
intent: If hover reveals a tooltip on one card but opens a popover on another visually identical card, users are surprised and distrustful of the interface.
nielsen:H4
?CON-015Apply consistent empty, error, and loading patterns everywheremoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Create canonical LoadingState, ErrorState, and EmptyState components in the design system and replace all custom variants with them.
intent: Bespoke loading spinners and error messages for each feature add visual noise and increase maintenance; a shared pattern set makes the app cohesive.
nielsen:H4
?CON-016Maintain a single source of truth for the design thememoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Centralise all design tokens in a single theme.ts or tokens.css; add an ESLint or Stylelint rule to prevent raw values that duplicate token names.
intent: Duplicated color or spacing values across multiple files diverge over time, causing subtle inconsistencies that are expensive to track down and fix.
css:C
?CON-017Use a consistent z-index scale to prevent stacking conflictsmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Define a z-index scale in the design system (e.g., base: 0, dropdown: 100, modal: 200, toast: 300); replace all hardcoded z-index values with tokens.
intent: Ad-hoc z-index values (z-index: 9999) create stacking conflicts between modals, tooltips, and toasts that are hard to debug and resolve.
css:C
?CON-018Use consistent breakpoints across all responsive rulesmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Define canonical breakpoints in the design system and expose them as CSS custom properties or Tailwind/Stitches theme values; replace all hardcoded media query widths.
intent: Different components breaking at different pixel values (768 px in one, 780 px in another) produce layouts that partially break at certain viewport widths.
css:C
?CON-020Follow platform conventions for native UI controlsmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Avoid fully replacing native controls (selects, checkboxes, date pickers) with custom implementations unless the native version is insufficient; when overriding, follow ARIA patterns.
intent: Users on a given platform (macOS, iOS, Android, Windows) have deeply ingrained expectations for scrollbars, checkboxes, and date pickers. Violating these conventions increases friction.
platform:P
?CON-021Use consistent form layouts across all forms in the appmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Create a shared Form layout component that enforces label position, field spacing, and button placement; use it for every form in the app.
intent: Forms with varying label positions (above vs inline), field widths, and button placements force users to re-orient on every form they encounter.
nielsen:H4
?CON-024Avoid duplicated or competing stylesheetsmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Audit the stylesheet loading order; remove or merge duplicate rule sets; use CSS Modules or a CSS-in-JS solution to scope component styles and avoid global conflicts.
intent: Multiple stylesheets that define conflicting rules for the same element produce unpredictable rendering and make refactoring dangerous.
css:C
?CPY-002Button labels start with an action verbmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Rewrite buttons as imperative verb phrases, e.g. 'Save changes' instead of 'Submit'.
intent: Verb-first labels ('Save changes', 'Delete account') set clear expectations; noun labels ('OK', 'Submit') leave users guessing what will happen.
nielsen:H1nielsen:H6
?CPY-004UI text uses plain language over technical jargonmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Substitute jargon with plain alternatives (e.g. 'payment method' instead of 'payment instrument') and test with users unfamiliar with the domain.
intent: Technical or insider jargon raises the cognitive load for all users and excludes those unfamiliar with the domain.
nielsen:H2
?CPY-005Terminology stays consistent throughout the interfacemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Create a shared glossary and audit the UI for synonym drift; settle on one term per concept.
intent: Switching between synonyms ('cart' / 'basket', 'log in' / 'sign in') forces users to re-map concepts and erodes trust.
nielsen:H4
?CPY-008Empty states explain the state and guide the next actionmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Write an empty-state heading ('No projects yet') plus a one-line explanation and a primary CTA button.
intent: A blank list or dashboard with no copy leaves users confused about whether it's a bug or an expected state.
nielsen:H1nielsen:H9
?CPY-010Error messages never blame the usermoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Rewrite errors to focus on the system or the data, not the user's actions.
intent: Blame ('You entered an invalid date') raises anxiety and damages trust. Neutral phrasing ('The date wasn't recognised') removes judgement.
nielsen:H9
?CPY-012Dates and numbers are formatted for the user's localemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Use Intl.DateTimeFormat and Intl.NumberFormat with the user's locale, or display unambiguous formats (e.g. 5 Apr 2026).
intent: Ambiguous formats like '04/05/06' mean different things in different regions, causing data-entry errors and mistrust.
nielsen:H2
?CPY-013Units and currency are always stated explicitlymoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Append units to all quantities; format currency with the ISO code or unambiguous symbol and decimal precision.
intent: A price of '99' or a duration of '30' is meaningless without its unit — users in different regions will assume different values.
nielsen:H2
?CPY-015Truncated text provides access to the full valuemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add a title attribute or tooltip to truncated elements, or provide an expand control that reveals the full text.
intent: Text clipped with ellipsis can hide critical information; users need a way to see the full string.
nielsen:H1
?CPY-017Reading level is appropriate for the target audiencemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Run copy through a readability checker and simplify sentences above grade 8 by shortening sentences and replacing multi-syllable words.
intent: Copy that exceeds the reading level of the audience reduces comprehension and creates barriers, especially for users with cognitive disabilities.
wcag:3.1.5
?CPY-019Page and section headings describe the content beneath themmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Rewrite vague headings ('Overview', 'Details') to be specific ('Pricing by plan', 'Shipping details for this order').
intent: Headings are the primary scanning aid; decorative or vague headings force users to read all body text to find what they need.
wcag:2.4.6nielsen:H6
?CPY-020Labels match the user's mental model, not internal namingmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Replace technical or product-internal labels with the terms your users actually use, informed by user research or usability testing.
intent: Internal code names or database field names surfaced in the UI require users to translate them — increasing cognitive load.
nielsen:H2
?CPY-021Icons used without text have a visible label or tooltipmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add a visible text label alongside icons in navigation and toolbars; at minimum provide a visible tooltip on hover/focus.
intent: Icon meaning is rarely universal; an icon-only control forces users to guess its function, increasing errors.
nielsen:H6wcag:1.1.1
?CPY-022CTAs set accurate expectations about the resulting actionmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Audit CTA copy against the actual resulting action; update labels to reflect scope (e.g. 'Start free 14-day trial' instead of 'Sign up free').
intent: A CTA that says 'Get started' for a 10-step signup flow misleads users and causes drop-off when they discover the true commitment.
nielsen:H1nielsen:H5
?FBK-003Interactive elements have a distinct hover statemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Apply :hover styles to all interactive elements so pointer users know what is clickable.
intent: A hover state signals interactivity. Without it, users can't confirm an element is clickable before committing to the click.
nielsen:H1platform:pointer-affordance
?FBK-006Disabled controls are visually distinct and conveyed to assistive technologymoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Style disabled controls with lower opacity or greyed colour and apply the disabled/aria-disabled attribute.
intent: Disabled elements that look identical to enabled ones waste user effort. AT must also know the element is unavailable, not just styled differently.
wcag:1.4.1aria:button
?FBK-007Long operations expose a progress indicatormoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Use a determinate progress bar when total work is known; use an indeterminate spinner with a label when it is not.
intent: Operations lasting more than ~2s without progress indication feel frozen, causing users to abandon or retry unnecessarily.
nielsen:H1
?FBK-008Success outcomes are confirmed with a visible messagemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Display a brief success toast or inline confirmation after every action that has a meaningful side-effect.
intent: Without explicit confirmation users are unsure their action (save, send, upload) succeeded, leading to repeated submissions.
nielsen:H1
?FBK-012The triggering control shows a loading state during async workmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Replace button content with a spinner (or add one alongside) and set disabled during pending requests; restore on completion.
intent: Disabling a button and showing its own spinner ties the loading feedback to the user's point of action and prevents double-submission.
nielsen:H1
?FBK-013Focused form fields are visually highlightedmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Apply a visible :focus / :focus-visible border or outline change to all form controls.
intent: Field focus styling (border colour change, glow) confirms to sighted users that their keyboard input will land in that field.
wcag:2.4.7
?FBK-015Icon-only controls have a visible tooltip on hover/focusmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add title attribute or a custom tooltip component to icon-only controls that appears on hover and focus.
intent: Icon buttons without labels leave sighted users who don't recognise the icon unable to determine the control's purpose.
nielsen:H6
?FBK-016Drag sources and valid drop targets are clearly affordedmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Apply cursor:grab / cursor:grabbing on drag handles and visually mark active drop targets with a border or background change.
intent: Drag-and-drop without visual affordance or drop-zone highlighting is a hidden interaction that users fail to discover or complete.
platform:pointer-affordance
?FBK-017Selected and checked states are clearly communicatedmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Style selected states with a checkmark, highlight, or border AND reflect the state in aria-selected / aria-checked.
intent: Users scanning a list of options need an unambiguous signal for what is currently selected or checked, both visually and semantically.
wcag:1.3.1aria:listbox
?FRM-004Personal data fields carry autocomplete tokensmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add autocomplete="name", "email", "tel", "street-address", "current-password", etc. to personal data fields.
intent: Without autocomplete tokens browsers can't offer saved credentials or address data, increasing effort for motor-impaired and cognitively impaired users.
wcag:1.3.5
?FRM-005Input elements use the most specific typemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Set input type to the most specific HTML value that matches the expected data format.
intent: Generic type=text forfeits browser-supplied validation, mobile keyboard optimisation, and AT cues for email, URL, and numeric data.
wcag:1.3.5html:H36
?FRM-010Display a summary of all errors at the top of the formmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. On submit failure inject an error summary above the form listing each error with an anchor to the field.
intent: Fixing errors one-by-one discovered by Tab-walking is slow; an error summary gives immediate context to all errors.
wcag:3.3.3
?FRM-012User-entered data is preserved across validation attemptsmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. On submit failure re-render the form with the user's existing values pre-populated.
intent: Clearing all fields on error forces re-entry of correct data, creating significant friction for motor-impaired users.
wcag:3.3.4
?FRM-013Submit buttons are protected from double submissionmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Disable the submit button on click and show a loading indicator until the operation resolves.
intent: Double-clicking a slow form can create duplicate records or charges; users with tremors are especially at risk.
nielsen:H5
?FRM-017Touch targets for form controls meet minimum sizemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Ensure controls are at least 24x24 CSS px; use padding or min-height/min-width to enlarge small controls.
intent: Tiny checkboxes and radio buttons are difficult to activate for users with motor impairments or on touch devices.
wcag:2.5.8
?FRM-018Tab order through the form is logical and sequentialmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Remove positive tabindex values; ensure DOM order matches visual order so natural tab sequence is correct.
intent: When tabindex rearranges focus or a visual layout places fields out of DOM order, keyboard-only users traverse a confusing sequence.
wcag:2.4.3
?FRM-019Successful submission gives clear confirmationmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Display a success message in an aria-live region (or navigate to a dedicated success page) after submission.
intent: Silent success after submitting leaves users unsure whether the action worked, causing resubmission and anxiety.
nielsen:H1wcag:4.1.3
?FRM-020Submit is disabled only when the reason is communicatedmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Either allow submit and validate on attempt, or display inline guidance explaining why the button is disabled.
intent: A disabled submit button with no explanation leaves users guessing which fields are incomplete.
wcag:3.3.2
?FRM-021Avoid auto-advancing focus between fields without user intentmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Remove auto-advance JavaScript; let users control focus movement through the form.
intent: Automatic focus jumps (e.g. after 3-digit phone segment) break reading flow and disorient screen-reader users mid-announcement.
wcag:3.2.1
?FRM-022Character-count indicators are announced to assistive technologymoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Link the counter to the input with aria-describedby and wrap it in an aria-live="polite" region.
intent: A visible character counter not linked to the input is silent to screen-reader users, who then exceed the limit unknowingly.
wcag:4.1.3
?I18N-005Currency values display the ISO code or unambiguous symbolmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Use Intl.NumberFormat with style:'currency' and the correct currency code, or display the ISO code (e.g. 'USD 99.00') explicitly.
intent: '$99' is ambiguous across USD, CAD, AUD, and dozens of other dollar currencies; users in the wrong locale misread prices.
nielsen:H2
?I18N-007CSS uses logical properties rather than physical directional valuesmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Replace left/right physical properties with their inline/block logical equivalents across all stylesheets.
intent: Physical properties (margin-left, padding-right) do not flip automatically for RTL; logical properties (margin-inline-start) do.
css:logical-properties
?I18N-008Bidirectional text is isolated correctly with Unicode bidi controlsmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Wrap mixed-direction inline content with dir='auto' or a <bdi> element to isolate its directionality.
intent: Mixing RTL and LTR text (e.g. an Arabic page with an English URL) can corrupt visual order without explicit bidi isolation.
wcag:1.3.2
?I18N-009UI containers accommodate at least 35% text expansionmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Use flexible, min-width-only sizing for text-bearing containers and test with pseudo-locales that expand string length by 40%.
intent: Translations of English strings are typically 30–50% longer in German and French; fixed-width containers clip or overflow translated text.
nielsen:H4
?I18N-010Locale-aware sorting and collation is applied to user-visible listsmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Replace Array.sort() comparisons on displayable strings with Intl.Collator.compare() configured for the user's locale.
intent: JavaScript's default sort() uses Unicode code points, producing incorrect alphabetical order in many languages (e.g. Swedish å, ä, ö).
nielsen:H2
?I18N-013The app provides a language switcher accessible from every pagemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add a language switcher (ideally in the header or footer) that lists languages in their native script and persists the selection.
intent: Users whose browser language differs from the default need a reachable mechanism to select their preferred language without losing context.
wcag:3.1.2nielsen:H7
?I18N-014Foreign-language passages are marked with a lang attributemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add lang='fr' (or the appropriate BCP 47 tag) to any element containing text in a different language from the page.
intent: Screen readers use the lang attribute to select the correct pronunciation engine; unmarked foreign text is mispronounced.
wcag:3.1.2
?I18N-015Time zones are handled explicitly and displayed to the usermoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Store timestamps in UTC and use Intl.DateTimeFormat with the user's time zone to convert times at display time.
intent: Displaying a time without a time zone causes confusion for users in different regions and is a frequent source of scheduling errors.
nielsen:H1
?I18N-017Name and address input forms accept international formatsmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Use a country-driven address form that shows and validates fields appropriate to the chosen country; avoid US-centric field requirements globally.
intent: Forms that require a US-style 5-digit ZIP code or two-character state code reject users from most countries.
nielsen:H2
?I18N-021Fonts cover all glyphs required by supported localesmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Audit font glyph coverage against each supported locale's character set; add subset font files or unicode-range @font-face rules for missing scripts.
intent: A font that lacks characters for a locale (e.g. missing CJK or Arabic glyphs) causes the browser to fall back to mismatched system fonts.
nielsen:H4
?I18N-022A fallback locale is defined for missing translationsmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Set a fallback locale (typically the source language) in the i18n library configuration and add automated checks for missing keys in CI.
intent: When a key is missing in the active locale, an untranslated fallback (or a blank string) is worse than displaying the source language string.
nielsen:H9
?MOT-003Keep animation durations between 150ms and 400msmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Audit animation duration tokens and clamp outliers to the 150–400ms window; use shorter values for small elements.
intent: Durations below 150ms feel abrupt; above 400ms they feel sluggish and obstruct the user from proceeding.
platform:material-motion-speed
?MOT-005Remove or contain infinite looping animationsmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Limit loops to loaders; stop or hide infinite animations once they are no longer needed or are off-screen.
intent: Persistent looping animation distracts users and can prevent focus for those with attention disorders.
wcag:2.2.2
?MOT-007Provide a non-animated fallback under reduced-motionmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Under reduced-motion, set opacity, transform, and transition to their final values directly rather than just slowing them down.
intent: Simply removing animation duration can leave UI in broken intermediate states; a well-designed fallback shows the end state immediately.
wcag:2.3.3
?MOT-008Animate only transform and opacity for performancemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Rearchitect animations to use transform (translate, scale) and opacity; use will-change sparingly to promote layers.
intent: Animating layout-triggering properties (width, height, top, left) forces reflow on every frame, causing jank on low-end devices.
css:will-change
?MOT-014Guard scroll-behavior: smooth with prefers-reduced-motionmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Wrap scroll-behavior: smooth in @media (prefers-reduced-motion: no-preference) or remove it entirely.
intent: CSS scroll-behavior: smooth causes long animated scrolls that disorient vestibular-sensitive users.
wcag:2.3.3css:scroll-behavior
?NAV-003The current location is indicated visually and programmaticallymoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Apply a visually distinct style to the active nav item and add aria-current='page' to its anchor element.
intent: Users lose their bearings when the active page or section is not clearly signalled; both sighted users and AT users need this anchor.
wcag:2.4.8nielsen:H1aria:aria-current
?NAV-004Breadcrumbs reveal location in deep hierarchiesmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Implement a breadcrumb component with a <nav aria-label='breadcrumb'> wrapping an ordered list of ancestor links.
intent: In sites with more than two levels of depth, breadcrumbs let users understand where they are and step back without losing context.
wcag:2.4.8aria:breadcrumb
?NAV-005Navigation placement is consistent across pagesmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Fix navigation to a consistent position (typically top or left) and do not relocate it for specific page types.
intent: Moving the primary navigation between pages forces users to re-locate it on every visit and breaks their spatial memory.
wcag:3.2.3nielsen:H4
?NAV-006Menu structure is shallow and logically groupedmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Reorganise navigation to three levels or fewer; group items by user goal rather than internal taxonomy.
intent: Navigation hierarchies deeper than three levels overwhelm users and increase time-to-task; logical grouping reduces scanning effort.
nielsen:H2nielsen:H8
?NAV-007Large sites offer a site search featuremoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add a search bar (role='search') to the header; connect it to a full-text or structured index of site content.
intent: Users who know what they want prefer direct search over browsing menus; without search on large sites they abandon.
nielsen:H7
?NAV-009Navigation labels use language the user understandsmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Conduct card-sorting or tree-testing to validate label comprehension; replace jargon with terms users recognise.
intent: Internal product names or technical terms as nav labels require users to guess their meaning, increasing scanning time and errors.
nielsen:H2
?NAV-012404 pages offer useful paths back into the sitemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Design a custom 404 page with a clear explanation, a search input, and links to the homepage and top-level sections.
intent: A dead-end 404 page causes abandonment; actionable links (homepage, search, categories) help users recover gracefully.
nielsen:H9
?NAV-013No pages are orphaned without a path from the navigationmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Audit the site map for orphan pages; add navigation links or cross-links to make every page reachable.
intent: Orphan pages are invisible to users navigating the site and are often missed by search indexers, wasting content investment.
nielsen:H1
?NAV-014Links have a clear visual affordance distinguishing them from body textmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Ensure links within body text are underlined by default and use a colour that passes WCAG contrast against the background.
intent: Underlines and colour are the canonical link affordances; removing both makes links invisible to users scanning for actions.
wcag:1.4.1nielsen:H6
?NAV-015Active and selected states are visually distinctmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add a distinct visual treatment (background, border, bold text, or icon change) for active/selected states, and expose it with aria-selected or aria-current.
intent: Without a visible active/selected state, users cannot tell which tab, menu item, or filter is currently active.
nielsen:H1wcag:2.4.8
?NAV-017Scroll position is restored when navigating backmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Persist scroll position in the history state or session storage and restore it on popstate events or equivalent router lifecycle hooks.
intent: Users who scroll a long list and open a detail page expect to return to the same scroll position, not the top of the list.
nielsen:H3
?NAV-019Page titles are descriptive, unique, and reflect the current statemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Set a unique, descriptive <title> for each route using the pattern 'Page name — Site name'; update it programmatically on SPA navigation.
intent: The <title> is the first thing a screen reader announces on page load and the text shown in browser tabs and history.
wcag:2.4.2nielsen:H1
?NAV-020Landmark regions delimit primary navigation, main, search, and footermoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Wrap site content in semantic HTML5 sectioning elements (<main>, <nav>, <header>, <footer>, <aside>) to create landmark regions.
intent: ARIA landmark roles let screen-reader users jump directly to the navigation, main content, or footer without tabbing through everything.
wcag:1.3.6aria:landmark-regions
?OPR-007Focus order follows logical reading sequencemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Align DOM source order with visual layout; avoid CSS techniques (grid/flex order) that decouple the two.
intent: When focus moves in a visually illogical order, keyboard users lose their place and the experience becomes disorienting.
wcag:2.4.3
?OPR-009Interactive targets meet the 24 × 24 px minimum sizemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Increase padding or min-width/min-height so clickable/tappable targets reach 24 × 24 px.
intent: Targets smaller than 24 × 24 CSS px are hard to hit for users with motor impairments or on touch screens.
wcag:2.5.8
?OPR-011Pointer-down does not trigger irreversible actionsmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Move action logic from mousedown/touchstart to click (or mouseup/touchend) and allow pointer-move cancellation.
intent: Activating actions on pointerdown prevents users from aborting accidental touches by moving the pointer away.
wcag:2.5.2
?OPR-013Device-motion actions have a non-motion alternativemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Provide UI buttons mirroring every motion action; expose a 'disable motion controls' preference.
intent: Shake, tilt, or gyroscope-based features cannot be used by people who have mounted devices or have limited movement.
wcag:2.5.4
?OPR-015Single-character key shortcuts can be remapped or disabledmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Gate single-character shortcuts on focus, or provide a settings UI to remap or disable them.
intent: Single-key shortcuts fire accidentally for speech-input users and people with motor conditions who brush keys.
wcag:2.1.4
?OPR-017Moving or auto-updating content can be paused, stopped, or hiddenmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add a clearly labelled pause/stop button adjacent to any auto-updating region.
intent: Carousels, tickers, and live feeds distract users with attention or cognitive disorders and interfere with screen readers.
wcag:2.2.2
?OPR-020Composite widgets use roving tabindex for internal navigationmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Implement the roving tabindex pattern: set tabindex=0 on the active item and tabindex=-1 on all others.
intent: Tabbing into every cell of a data grid or every item in a listbox creates an exhausting, counter-intuitive keyboard experience.
wcag:2.1.1aria:grid
?OPR-022Custom controls have a discernible focus style in high-contrast modemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Set outline: 2px solid on :focus-visible in addition to any box-shadow; test with forced-colors:active.
intent: Windows High Contrast and forced-colours modes strip CSS backgrounds/box-shadows; focus rings relying on them become invisible.
wcag:2.4.7css:forced-colors
?OPR-023Pages have a meaningful sequence of headings for in-page navigationmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Audit heading structure; use h1 for the page title and nest sub-sections with h2–h6 in order.
intent: Screen-reader users navigate by headings; a missing or misordered heading hierarchy makes long pages difficult to scan.
wcag:2.4.6html:H42
?PER-005Video conveying visual information offers audio descriptionmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add an audio-description track, or a text alternative covering the visual-only content.
intent: Blind users miss on-screen action that isn't narrated; audio description fills that gap.
wcag:1.2.5
?PER-006Audio-only content has a transcriptmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Publish a transcript adjacent to any audio-only content.
intent: Podcasts and audio clips are inaccessible to deaf users without a readable transcript.
wcag:1.2.1
?PER-008Complex graphics (charts, diagrams) have a long descriptionmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Pair complex visuals with a data table or a descriptive summary.
intent: A one-line alt can't convey a chart; users need the underlying data or a longer description.
wcag:1.1.1
?PER-010Instructions don't rely on sensory characteristics alonemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add a text label or name to anything referenced purely by appearance or position.
intent: 'Click the round button on the right' fails users who can't perceive shape, size, or position.
wcag:1.3.3
?PER-011Reading and DOM order convey a meaningful sequencemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Keep source order meaningful; avoid order/flex-order tricks that desync from reading order.
intent: When CSS reorders content visually, AT still follows DOM order; a mismatch garbles meaning.
wcag:1.3.2
?PER-015User text-spacing overrides don't break the layoutmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Avoid fixed heights on text containers; let them grow with content.
intent: Some users force larger line/letter/word spacing; rigid containers then clip text.
wcag:1.4.12
?PER-016Content on hover or focus is dismissable and persistentmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Make tooltips dismissable with Esc, hoverable, and persistent until intentionally closed.
intent: Tooltips/popovers that vanish or can't be dismissed trap low-vision and motor users.
wcag:1.4.13
?PER-017Audio doesn't autoplay (or can be stopped immediately)moderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Avoid autoplaying audio; if unavoidable, provide an obvious, reachable stop control.
intent: Unexpected audio competes with screen readers and startles users.
wcag:1.4.2
?PER-019Live media provides real-time captionsmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Provide live captioning for synchronous media.
intent: Live streams without captions exclude deaf users from time-sensitive content.
wcag:1.2.4
?PER-021Iframes carry a descriptive titlemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add a concise title to each iframe, e.g. title="Checkout payment form".
intent: An untitled <iframe> is announced as 'frame' with no hint of its contents.
wcag:4.1.2html:H64
?PER-022Status messages are exposed without moving focusmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Render transient status in an aria-live="polite" region (role=status/alert).
intent: Toasts and inline status need a live region, or AT users never hear them.
wcag:4.1.3
?PRF-001Reserve space for images to prevent layout shiftmoderatestatic
1 <img> without width+height (or aspect-ratio) — a layout-shift (CLS) risk.
Fix. Add width and height attributes to every <img>, or set aspect-ratio in CSS to match the image's intrinsic ratio.
evidence: index.html:10:5
intent: Images without explicit dimensions cause the browser to reflow the page once they load, degrading CLS. Reserving space via width/height attributes or CSS aspect-ratio eliminates that shift.
cwv:CLS
?PRF-007Keep Cumulative Layout Shift below 0.1moderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Reserve space for images, ads, and embeds; avoid inserting content above existing content after load.
intent: Unexpected layout shifts frustrate users who mis-click on moved content. A CLS score above 0.1 indicates the page is visually unstable during load.
cwv:CLS
?PRF-008Serve images in modern formats (WebP / AVIF)moderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Convert images to WebP or AVIF at build time and use <picture> with <source type="image/avif"> and <source type="image/webp"> for broad support.
intent: WebP and AVIF deliver equivalent visual quality at 25–50 % smaller file sizes compared to JPEG/PNG, directly reducing LCP time and data usage.
cwv:LCP
?PRF-009Use srcset and sizes for responsive imagesmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Generate 2–4 width variants per image and declare them in srcset; set sizes to match your CSS layout breakpoints.
intent: Serving a single large image to all devices forces mobile users to download far more bytes than they display. srcset lets the browser pick the optimal resolution.
cwv:LCP
?PRF-010Preconnect to critical third-party originsmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add <link rel="preconnect" href="https://..." crossorigin> for each critical third-party origin.
intent: DNS lookup, TCP handshake, and TLS negotiation each add latency before the first byte of a cross-origin resource. A preconnect hint pays that cost early.
cwv:LCP
?PRF-011Preload the LCP image or largest hero assetmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Identify the LCP image and add <link rel="preload" as="image" href="..." imagesrcset="..." imagesizes="..."> to the head.
intent: The browser discovers the LCP image late in the waterfall. A preload hint starts the fetch as early as possible, directly reducing LCP time.
cwv:LCP
?PRF-013Tree-shake and minify production bundlesmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Enable minification and ensure package.json sideEffects: false is set for libraries; verify with a bundle analyzer that unused exports are absent.
intent: Dead code and unminified output inflate JS payload, increasing parse time and INP. Tree-shaking and minification can cut bundle size by 30–60 %.
cwv:INP
?PRF-014Enable gzip or Brotli compression on text assetsmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Configure the server or CDN to apply Brotli for modern browsers and gzip as fallback for all compressible text assets.
intent: Compressed HTML, CSS, and JS transfer 60–80 % fewer bytes over the network, reducing both LCP and TTFB for users on slow connections.
cwv:TTFB
?PRF-015Set long cache headers for immutable static assetsmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Enable content-hash fingerprinting in the bundler and set Cache-Control: public, max-age=31536000, immutable on those assets at the CDN/server level.
intent: Static assets with content-hashed filenames never change, so a max-age of one year eliminates repeat download latency for returning users.
cwv:LCP
?PRF-016Budget and monitor JavaScript bundle sizemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add size-limit or bundlesize to CI with per-entry thresholds; fail the build when any bundle exceeds the agreed budget.
intent: Unbounded JS growth silently degrades INP and TTI. A size budget enforced in CI prevents regressions before they reach users.
cwv:INP
?PRF-017Defer non-critical third-party scriptsmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add defer or async to every third-party <script> tag; for scripts that must run in order, load them programmatically after the page is interactive.
intent: Analytics, chat widgets, and ad scripts that block the parser delay FCP and LCP. Deferring or async-loading them keeps the critical path unobstructed.
cwv:FCP
?PRF-018Inline critical CSS and defer the restmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Use a critical CSS extractor (critical, Critters) to inline above-fold styles at build time and load the remaining stylesheet asynchronously.
intent: Render-blocking stylesheets prevent the browser from painting until they download. Inlining above-fold critical CSS eliminates that block for the first paint.
cwv:FCP
?PRF-020Debounce or throttle expensive event handlersmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Wrap event callbacks in lodash.debounce or throttle, or use passive event listeners with requestAnimationFrame for visual updates.
intent: Scroll, resize, and input events fire hundreds of times per second. Unthrottled handlers that do heavy work block the main thread and directly increase INP.
cwv:INP
?PRF-022Limit and audit third-party scriptsmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Audit all third-party scripts quarterly; remove unused ones and load survivors with defer or a facade pattern to prevent render blocking.
intent: Every third-party script is an uncontrolled dependency that can block the main thread, increase CLS, and introduce security risk. Fewer scripts mean faster, safer pages.
cwv:INP
?PRF-024Minimize unnecessary component re-rendersmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Profile with React DevTools Profiler; apply React.memo, useMemo, and useCallback at bottleneck components; avoid creating new object/array literals in render.
intent: Components that re-render when their props and state have not meaningfully changed waste CPU cycles and can push INP over budget in complex UIs.
cwv:INP
?PRF-026Stream or server-render time-to-first-byte critical contentmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Use SSR (Next.js, Remix, Astro) or streaming (React Suspense with renderToPipeableStream) for content above the fold; hydrate interactivity on the client.
intent: Pure client-rendered pages show nothing until JS downloads, parses, and executes. SSR or streaming HTML lets the browser paint meaningful content immediately, improving TTFB and LCP.
cwv:TTFB
?ROB-006Status messages are announced via ARIA live regionsmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Inject transient messages into a pre-existing aria-live="polite" or aria-live="assertive" region.
intent: Dynamic status (save confirmation, cart count, error toast) is never announced to AT unless wrapped in a live region.
wcag:4.1.3aria:live-regions
?ROB-007Prefer native HTML elements over ARIA overridesmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Replace <div role=button> with <button>, <span role=checkbox> with <input type=checkbox>, etc.
intent: Native elements (<button>, <input>, <select>) carry built-in semantics and behaviour; ARIA overrides add complexity and risk mistakes.
aria:aria-in-html
?ROB-008Abstract ARIA roles are not assigned to elementsmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Replace abstract roles with a concrete child role (e.g. role=button instead of role=widget).
intent: Abstract roles (command, composite, input, landmark, range, roletype, section, sectionhead, select, structure, widget, window) are taxonomy nodes, not assignable roles.
aria:abstract-roles
?ROB-012Do not override native element semantics with a conflicting rolemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Remove conflicting role attributes from native elements; use a neutral <div> or <span> if a role-free container is needed.
intent: Assigning role=presentation or role=none to a native semantic element strips its semantics, removing keyboard and AT affordances.
aria:aria-in-htmlwcag:4.1.2
?ROB-013ARIA attribute values conform to their specified typemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Audit aria-* values against the WAI-ARIA spec; use boolean strings ('true'/'false') not JS booleans.
intent: Invalid attribute values (aria-checked="yes", aria-expanded="0") are either ignored or misinterpreted by AT.
aria:state_prop_values
?ROB-014Landmark regions are not duplicated without distinct labelsmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add aria-label="Primary" / aria-label="Breadcrumb" etc. to disambiguate repeated landmark roles.
intent: Two unlabelled <nav> or <aside> landmarks are indistinguishable in the AT landmark list, confusing users who rely on landmark navigation.
aria:landmark-regionswcag:1.3.6
?ROB-015HTML nesting follows the content modelmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Validate HTML with the Nu Html Checker; fix interactive-content-inside-interactive-content errors first.
intent: Invalid nesting (e.g. <p> inside <a>, <div> inside <p>) causes browser parsing differences and breaks AT tree construction.
wcag:4.1.1html:content-model
?RSP-005Use fluid grids with %, fr, or minmax()moderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Replace fixed-width grid columns with minmax(min-content, 1fr) or percentage-based tracks.
intent: Fixed-column grids break at unanticipated widths; fluid units let the grid adapt continuously.
css:grid
?RSP-006Make images flexible with max-width: 100%moderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add img, picture { max-width: 100%; height: auto; } to the global reset or base stylesheet.
intent: Images without a width constraint overflow their container on narrow viewports, causing horizontal scrolling.
css:responsive-images
?RSP-009Maintain content parity across breakpointsmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Replace display:none hiding of informational content with a disclosure pattern (accordion, drawer) on small screens.
intent: Hiding significant content on mobile deprives those users of information available on desktop.
wcag:1.3.4
?RSP-014Honour safe-area insets on notched devicesmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left) to fixed/sticky elements.
intent: On notched phones, content that ignores safe-area-inset-* is obscured by the notch or home indicator.
platform:apple-safe-area
?RSP-016Data tables are scrollable or restructured on small screensmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Wrap <table> in a div with overflow-x: auto, or use a responsive pattern (card rows) below a threshold breakpoint.
intent: Wide tables overflow narrow viewports without a scroll wrapper, causing double horizontal scrolling.
wcag:1.4.10
?RSP-017Sticky headers do not consume excessive small-viewport heightmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Collapse or thin the sticky header on small screens, hiding secondary nav and keeping only the essential bar.
intent: A sticky header that is 25%+ of the viewport on a phone leaves little room for content and makes the page feel cramped.
nielsen:H8
?RSP-018Modals and bottom sheets fit within small viewport dimensionsmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Set max-height: calc(100dvh - var(--safe-area)) with overflow-y: auto on modal content areas.
intent: A full-page modal that overflows a 375px screen forces users to scroll inside a scroll, which is disorienting.
wcag:1.4.10
?RSP-020Provide srcset and sizes for responsive imagesmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add srcset with 2–4 width descriptors and a sizes attribute that matches the image's layout width at each breakpoint.
intent: Serving a 2000px image to a 375px phone wastes bandwidth and slows load; srcset lets the browser pick the right size.
html:responsive-imagescwv:LCP
?RSP-022Handle mobile viewport height robustly (dvh or fallback)moderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Replace height: 100vh on full-page layouts with height: 100dvh, providing height: 100vh as a fallback for older browsers.
intent: On mobile browsers, 100vh includes the address bar and causes content to be clipped; 100dvh or a JS fallback gives the actual visible height.
css:viewport-units
?SEM-003Each page contains exactly one main landmarkmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Remove duplicate <main> elements; keep exactly one wrapping the primary content.
intent: Multiple <main> elements (or role=main) confuse the 'skip to main content' target and disorient screen-reader users.
wcag:1.3.1aria:main
?SEM-004Heading levels nest without skippingmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Fix heading hierarchy so each nested section uses the next consecutive level.
intent: Skipping from h2 to h4 breaks the implicit document tree; screen-reader users navigating by heading lose orientation.
wcag:1.3.1html:H42
?SEM-006Lists use ul, ol, or dl elementsmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Replace list-like <div> structures with <ul>, <ol>, or <dl> as appropriate.
intent: Screen readers announce list semantics (count, position) only when real list elements are used; divs masquerading as lists lose that.
wcag:1.3.1html:H48
?SEM-008Tables are not used for visual layoutmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Replace layout tables with CSS grid or flexbox and remove any role=presentation hack.
intent: Layout tables confuse the table-navigation shortcuts of screen readers and expose non-semantic row/column relationships.
wcag:1.3.1
?SEM-010Form fields are grouped with fieldset and legendmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Wrap related inputs (especially radio/checkbox groups) in <fieldset><legend> … </fieldset>.
intent: Related controls (radio groups, address blocks) without a fieldset/legend lose their shared context when navigated individually by screen readers.
wcag:1.3.1html:H71
?SEM-016Breadcrumbs use an ordered list with aria-label='Breadcrumb'moderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Render breadcrumbs as <nav aria-label="Breadcrumb"><ol>…<li aria-current="page">…</li></ol></nav>.
intent: An unlabelled or unordered breadcrumb trail loses the implied sequence and isn't announced as navigation by AT.
wcag:1.3.1aria:breadcrumb
?SEM-019Prefer semantic elements over ARIA roles on divsmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Replace role-bearing <div>/<span> elements with the matching native HTML element.
intent: ARIA roles on <div> are a fragile shim; native elements supply role, state, and keyboard behavior in one; a missing script leaves a roleless div.
wcag:4.1.2aria:no-aria-hidden-body
?SEM-021Multiple nav regions carry unique accessible namesmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add distinct aria-label values (e.g. "Primary", "Footer") to each <nav> element.
intent: Two unlabelled <nav> elements are announced identically, leaving AT users no way to distinguish primary from secondary navigation.
wcag:1.3.1aria:navigation
?SPC-002Use a consistent 4/8px spacing scale throughoutmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Define a spacing scale (e.g. 4, 8, 12, 16, 24, 32, 48px) and enforce it via design tokens or a utility class system.
intent: A shared base-4 or base-8 scale ensures visual harmony; arbitrary spacing values create visual inconsistency across the UI.
nielsen:H4
?SPC-004Drive spacing from design tokens, not raw literalsmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Introduce a spacing token set (CSS variables, Tailwind scale, or similar) and migrate hard-coded values to tokens.
intent: Token-driven spacing lets the entire scale update from one place, avoiding drift between design and code.
css:custom-properties
?SPC-006Maintain consistent gutters and column gaps across the gridmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Declare gutters from the spacing scale and apply the same values consistently across all grid usage.
intent: Inconsistent column/row gaps make layouts feel unplanned and break visual alignment across sections.
nielsen:H4
?SPC-007Preserve consistent vertical rhythm between text blocksmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Define margin-block values for headings and paragraphs from the spacing scale and apply them uniformly.
intent: Uneven vertical spacing between headings, paragraphs, and sections disrupts reading flow and looks unpolished.
css:margin-block
?SPC-008Align elements to a layout gridmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Adopt a 12- or 16-column CSS grid at the page level and place all major sections within its tracks.
intent: Off-grid alignment creates a visually noisy page; a column grid ensures elements line up across sections.
nielsen:H4
?SPC-013Spacing reflects grouping and relationship (proximity law)moderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Apply the Law of Proximity: increase spacing between sections and reduce spacing within them.
intent: Related items should be closer together than unrelated ones; mismatched spacing inverts the perceived grouping.
nielsen:H4
?SPC-014Cap container max-width for a readable measuremoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add max-width: 75ch (or ~760px) to prose containers and center them within the viewport.
intent: Lines longer than ~75 characters are harder to read; an unconstrained max-width degrades long-form readability.
wcag:1.4.8
?SPC-018Maintain consistent section-to-section spacing rhythmmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Define --section-gap (e.g. 64px) and apply it as margin-block or padding-block on every page section.
intent: Page sections with erratic vertical spacing feel unfinished; a consistent between-section gap creates a satisfying page rhythm.
nielsen:H4
?SPC-020Give clickable/tappable areas sufficient size to avoid crampingmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Use padding to expand small controls' hit areas and add at least 8px gap between adjacent interactive elements.
intent: Controls placed too close together or made too small cause mis-taps, particularly on touch screens.
wcag:2.5.8
?STA-001Render lists with stable keysmoderatestatic
1 .map(...) may render list items without a key prop — React relies on stable keys for correct, performant reconciliation.
Fix. Use a stable unique identifier from the data (e.g., item.id) as the key; never use the array index unless the list is static and never reordered.
evidence: ProductList.jsx:5:8
intent: React and similar frameworks use keys to identify which list items changed. Unstable or index-based keys cause unnecessary unmounts and remounts, losing component state and triggering jarring UI flashes.
html:H
?STA-003Use skeleton placeholders to reduce perceived load timemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Build skeleton components that mirror the content structure (same number of lines, image slots) and render them during the loading state.
intent: Skeleton screens set correct layout expectations before data arrives, making loading feel faster than a blank area or spinner alone.
nielsen:H1
?STA-004Provide empty-state guidance with a clear CTAmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Design empty states for every list and data view; include an illustration or icon, a short explanation, and a primary CTA (e.g., 'Add your first item').
intent: A blank screen with no explanation when there is no data leaves users confused. A helpful empty state explains the situation and guides the next action.
nielsen:H9
?STA-006Handle offline state gracefullymoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Listen to the navigator.onLine event and show an offline banner; for write actions, queue them with a service worker or local state and replay on reconnect.
intent: Users on unreliable connections lose network mid-session. Without offline handling the app silently fails; with it, users understand the situation and can resume when connected.
nielsen:H1
?STA-007Implement optimistic UI with rollback on failuremoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Capture the previous state before an optimistic update and restore it in the onError callback of the mutation; show a toast explaining the failure.
intent: Applying a mutation immediately (optimistic update) makes the UI feel instant. Providing rollback ensures the UI stays consistent if the server rejects the change.
nielsen:H5
?STA-009Confirm successful operations with visible feedbackmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Show a dismissible success toast or inline confirmation message after each completed mutation; update the relevant UI to reflect the new state.
intent: Without success confirmation, users are unsure whether their action took effect and may repeat it or feel uncertain about the app's reliability.
nielsen:H1
?STA-011Offer a retry affordance on transient failuresmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add a retry callback to error UI components; consider automatic exponential-backoff retry for idempotent GET requests.
intent: Network errors are often transient. Giving users a one-click retry path is more helpful than asking them to refresh the entire page.
nielsen:H9
?STA-013Provide a helpful 404 page with navigation optionsmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Create a custom 404 route with the app's header/footer, a clear explanation, and links to the home page, search, and popular destinations.
intent: A generic server 404 page with no branding or context disorients users. A custom 404 maintains trust and guides users to valid content.
nielsen:H9
?STA-014Reflect form submitting state in the submit buttonmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Set the submit button to disabled with a 'Submitting…' label or spinner while the form mutation is in flight.
intent: A form whose submit button stays active and unchanged during submission invites duplicate submissions and gives no feedback that the request is processing.
nielsen:H1
?STA-015Show a loading indicator for infinite-scroll fetchesmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Render a loading spinner or skeleton rows at the list end while isFetchingNextPage is true; show a terminal message when hasNextPage is false.
intent: Without a visible indicator at the bottom of an infinite list, users scroll into empty space and do not know whether more content is coming or the list is exhausted.
nielsen:H1
?STA-016Distinguish zero-results from an error statemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Use separate state variables (isEmpty, isError) and render distinct components: an empty-state with query tips for no results, an error component with retry for failures.
intent: Showing the same blank UI for 'no results found' and 'request failed' misleads users — one invites refining the query, the other requires a retry or support contact.
nielsen:H6
?STA-017Show a first-run or onboarding empty state for new usersmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Check for empty state on first login and render an onboarding card or checklist that explains what the user can do and provides a clear first step.
intent: A new user who sees an empty dashboard with no guidance may think the product is broken. A first-run empty state teaches the product's value and the first action to take.
nielsen:H6
?STA-019Offer undo for reversible destructive actionsmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Delay the destructive API call by 4–8 seconds; display an undo toast during the window; cancel the call if the user clicks Undo.
intent: An undo affordance is less disruptive than a confirmation dialog but still prevents accidental data loss. Users trust apps that let them correct mistakes.
nielsen:H3
?STA-020Handle slow or timed-out requests gracefullymoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Set an AbortController timeout on fetch calls (e.g., 15 s) and treat AbortError as a timeout; show 'Taking longer than expected — Retry?' to the user.
intent: A request that hangs indefinitely with no feedback looks like a freeze. Surfacing a timeout message with a retry path keeps users informed and in control.
nielsen:H1
?TRU-005Credential fields carry correct autocomplete tokensmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Apply the appropriate autocomplete token to every credential-related input following the HTML living standard.
intent: The autocomplete attribute signals to browsers and password managers how to auto-fill correctly, reducing user friction and mis-filling.
html:autofillowasp:A07:2021
?TRU-009Data usage is explained in plain language near collection pointsmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add a concise 'Why we need this' note or help text near sensitive data collection points.
intent: Users who don't understand why data is collected cannot give meaningful consent, and may abandon forms out of distrust.
platform:gdprnielsen:H6
?TRU-011Fake urgency and artificial scarcity are not usedmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Remove or make optional any urgency/scarcity indicators that are not backed by real-time accurate data.
intent: Manufactured countdown timers and false 'Only 2 left!' claims are manipulation patterns that damage trust when users see through them.
nielsen:H3
?TRU-015Privacy policy and terms of service links are easily discoverablemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add Privacy Policy and Terms links to the site footer so they appear on every page without cluttering the main navigation.
intent: Users who can't find legal documents distrust the product; regulators require them to be accessible from every page.
platform:gdpr
?TRU-017Unsubscribe and opt-out mechanisms are easy to usemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Implement a single-click unsubscribe in emails and a self-serve account deletion flow in the settings area.
intent: Burying unsubscribe behind multiple steps or dark patterns violates CAN-SPAM/GDPR and destroys the trust of users who have lost interest.
platform:gdpr
?TRU-018Permissions are requested in context with a rationalemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Show a brief explanation before triggering the browser permission prompt; only request permissions at the moment of use.
intent: Browser permission prompts (camera, location, notifications) shown without context and explanation are almost universally denied.
platform:permissions-uxnielsen:H6
?TRU-021A clear and accessible sign-out action is providedmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Place a sign-out link in the primary account/user menu and ensure it is reachable without scrolling on every authenticated page.
intent: Users on shared devices need to terminate their session; a missing or hidden sign-out leaves them unable to protect their account.
owasp:A07:2021nielsen:H3
?TRU-022Only the minimum required data is collectedmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Audit every data-collection point; remove or make optional any field whose data is not actively used by the product.
intent: Collecting data beyond what is strictly needed for the stated purpose is a GDPR violation and increases breach impact.
platform:gdpr
?TYP-001Keep body text at a legible sizemoderatestatic
1 font-size below 12px — small text is hard to read, especially on mobile and for low-vision users.
Fix. Set base body font-size to 1 rem and express all text sizes in rem or em so they respect the user's browser preferences.
evidence: styles.css:4:10
intent: Text below 16 px (1 rem) is difficult for many users to read and fails WCAG resize requirements when the browser default is overridden with px units.
wcag:1.4.4
?TYP-002Limit line length to 45–75 characters for body textmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Set a max-width of ~65ch on body text containers to keep the measure in the optimal legibility range.
intent: Lines shorter than 45 characters cause disruptive eye movement; lines longer than 75 make it hard to track to the next line, both slowing reading speed.
nielsen:H2
?TYP-003Body text uses a line-height of approximately 1.5moderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Set line-height: 1.5 on the body or prose container as a unitless value so it scales proportionally with font-size.
intent: Tight line-height forces lines too close together, making it hard to track from the end of one line to the start of the next.
wcag:1.4.12
?TYP-009Long passages of text avoid all-caps stylingmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Reserve all-caps for abbreviations, short UI labels, or decorative headings; never apply it to prose paragraphs.
intent: All-caps text reduces reading speed by ~10–25% because readers lose word-shape cues; it is appropriate only for short labels or headings.
nielsen:H2
?TYP-011Font sizes use relative units (rem or em) rather than pxmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Convert all font-size px values to rem (px ÷ 16) so text scales with the user's browser preference.
intent: Fixed px sizes ignore the user's browser font-size preference, preventing zoom-based text scaling required by WCAG 1.4.4.
wcag:1.4.4
?TYP-012Body text is not fully justifiedmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Remove text-align: justify from prose; if justified text is a design requirement, enable hyphens: auto alongside it.
intent: CSS text-align: justify creates uneven word spacing ('rivers') that disrupts reading flow, particularly at narrow column widths.
nielsen:H2
?TYP-017Measure (line length) remains legible on mobile viewportsmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Ensure mobile containers have at least 16 px of horizontal padding and that prose text isn't forced to font-sizes below 0.875 rem.
intent: At 320–375 px screen widths, full-width prose can drop to 25–35 characters per line, well below the 45-character minimum for comfortable reading.
wcag:1.4.10
?TYP-018Heading levels follow a consistent visual scalemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Map heading levels to type-scale steps in descending order and lock the mapping in a shared token so it cannot be broken per-component.
intent: Headings that jump erratically in size (H2 larger than H1, or H3 the same size as H2) destroy the information hierarchy they're supposed to communicate.
nielsen:H2
?TYP-019Body text avoids excessive negative letter-spacingmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Remove negative letter-spacing from body text; apply it only to display headings of 32 px or larger.
intent: Negative letter-spacing on small body text collapses inter-character space below legibility thresholds, especially for users with dyslexia.
wcag:1.4.12
?UND-006Navigation mechanisms are consistent across pagesmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Extract navigation into a shared component; never reorder links between routes without strong reason.
intent: Relocating the main menu or changing link order between pages forces users to relearn the site on every view.
wcag:3.2.3
?UND-007Components with the same function are identified consistentlymoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Define a component naming convention and enforce it via a shared design system.
intent: When a 'Delete' icon uses a different label or icon on two screens, users — especially those with cognitive impairments — cannot reliably predict behavior.
wcag:3.2.4
?UND-011Error suggestions tell users exactly how to fix the problemmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Expand error messages to include the expected format or a valid example value.
intent: Saying a field is invalid without explaining the expected format leaves users guessing, which is especially harmful for those with cognitive impairments.
wcag:3.3.3
?UND-013Previously entered information is not re-requested unnecessarilymoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Carry forward validated input across steps; allow users to confirm rather than retype repeated data.
intent: Requiring users to re-enter name, address, or other data they already provided increases cognitive load and error rate.
wcag:3.3.7
?UND-017Component behavior matches established conventionsmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Reference the ARIA Authoring Practices Guide widget patterns; deviate only with clear user-facing cues.
intent: Widgets that look standard but behave unexpectedly violate user mental models, causing errors and frustration.
wcag:3.2.4aria:patterns
?UND-018Instructions are expressed in plain language, not just visual cuesmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Rewrite sensory-only instructions to include text labels (e.g. 'Required fields are marked with *').
intent: Instructions like 'fill in the red fields' or 'click the icon below' fail for users who cannot see colour, shape, or position.
wcag:1.3.3wcag:3.3.2
?UND-020Input format requirements are communicated before submissionmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add hint text below the input label listing format requirements; keep it visible during input.
intent: Discovering format requirements only after an error forces an extra round-trip and extra cognitive effort, particularly for users with memory impairments.
wcag:3.3.2
?UND-022Required fields are clearly and consistently indicatedmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add aria-required="true" to all required inputs and include a legend explaining the required indicator.
intent: If required fields are not labelled, users miss them; inconsistent indicators (asterisk on some, 'Required' on others) add confusion.
wcag:3.3.2html:H90
?VIS-002Primary and secondary buttons are visually distinctmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Define separate button variants (filled vs outlined, or strong vs subtle) and apply them consistently across the product.
intent: When primary and secondary buttons share the same visual weight, users can't quickly identify the recommended action, leading to hesitation or errors.
nielsen:H4
?VIS-003Type scale signals content importancemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Adopt a modular type scale (e.g. Major Third or Perfect Fourth) and map heading levels to it so hierarchy is self-evident.
intent: A typographic scale that varies size and weight communicates structure at a glance, reducing the cognitive load of parsing a page.
nielsen:H2
?VIS-004Size, weight, and color together encode element prioritymoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Combine size or weight with color so hierarchy survives both colorblindness and greyscale rendering.
intent: Relying on a single visual dimension (color alone, or size alone) to signal importance makes hierarchy fragile for users with color-vision deficiencies or in different display contexts.
nielsen:H4
?VIS-005Proximity groups semantically related elementsmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Apply a consistent spacing scale where same-group gaps are smaller than between-group gaps.
intent: Gestalt proximity means users perceive nearby elements as belonging together; misaligned spacing creates false groupings and confusion.
nielsen:H2
?VIS-007Simultaneous emphasis is limited to avoid noisemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Reduce the quantity of emphasized elements until only the most critical items remain high-contrast or large.
intent: When many elements are bold, large, or brightly colored at once, none of them reads as important — the emphasis is meaningless.
nielsen:H8
?VIS-009Peer elements share consistent visual weightmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Extract peer-element styling into a shared component or token and prohibit ad-hoc overrides.
intent: Items at the same conceptual level (navigation items, cards in a grid) should look equal so the hierarchy doesn't mislead users about their relative importance.
nielsen:H4
?VIS-011Layout supports F- or Z-pattern scanningmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Position the headline top-left, key benefit center-or-right, and the primary CTA at the terminal scan point of the pattern.
intent: Eye-tracking research shows users scan in predictable F or Z shapes; placing key content off these paths means it will be missed.
nielsen:H2
?VIS-012Complex workflows use progressive disclosuremoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Move low-frequency settings behind a 'More options' control or secondary screen rather than displaying them inline.
intent: Surfacing every option at once overwhelms users; revealing detail only as needed keeps the interface approachable.
nielsen:H8
?VIS-015The hero or masthead communicates a single key messagemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Trim the hero to headline + single-sentence value proposition + primary CTA; move secondary CTAs below the fold.
intent: A hero crammed with competing headlines, graphics, and CTAs causes fixation loss — users leave having absorbed nothing.
nielsen:H4
?VIS-016Information density matches the user's task contextmoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Define separate density presets (compact / default / comfortable) and apply the appropriate one based on the task context of each view.
intent: Developer dashboards tolerate high density; consumer homepages require breathing room — forcing a single density across all contexts impairs usability.
nielsen:H2
?VIS-019Data is emphasized over decorative chromemoderatenone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Reduce border weight, icon size, and background fill so that data values are the highest-contrast element in their container.
intent: Heavy borders, background fills, and excessive iconography around data can overshadow the actual numbers or content, inverting the intended hierarchy.
nielsen:H8
?CLR-001Centralise colors as design tokensminorstatic
3 raw hex color value(s) and no CSS custom properties — centralise colors as tokens for consistent theming and contrast control.
Fix. Extract every color value into a CSS custom property or design-token file and replace inline literals with token references throughout the codebase.
evidence: styles.css:4:33 styles.css:4:54 styles.css:5:12
intent: Hard-coded hex values scattered through component files make it impossible to verify or change contrast consistently across the product.
css:custom-properties
?CLR-002Honour the OS light/dark preference via prefers-color-schememinorstatic
No prefers-color-scheme handling detected — consider honouring the user's OS light/dark preference.
Fix. Define a dark-mode token layer inside @media (prefers-color-scheme: dark) and ensure every foreground/background pair is re-evaluated for contrast.
intent: Users who select dark mode at the OS level expect applications to follow suit automatically; ignoring the preference is jarring and can cause eye strain at night.
css:prefers-color-scheme
?CLR-010Disabled state elements remain perceivableminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Apply a minimum opacity of 0.38 or use a low-contrast token that keeps disabled elements visible while clearly inactive.
intent: Disabled controls should not vanish entirely; users need to see what exists (even if inert) to understand the available interface.
wcag:1.4.3
?CON-002Remove TODO/FIXME from shipped UIminorstatic
1 TODO/FIXME/HACK marker(s) in UI source — unfinished work that may surface as rough edges.
Fix. Resolve or ticket all TODO/FIXME comments before merging to main; configure an ESLint rule (no-warning-comments) to prevent new ones from being merged.
evidence: ProductList.jsx:9:8
intent: TODO and FIXME comments in production code signal unfinished work and can inadvertently reveal internal implementation details or security issues.
html:H
?CON-011Follow component naming conventions across the codebaseminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Adopt a naming convention (PascalCase for React components, kebab-case for files) and enforce it with ESLint; rename outliers in a dedicated refactor PR.
intent: Inconsistent naming (e.g., UserCard vs CardUser vs user-card) makes it harder to discover existing components and increases the chance of duplication.
nielsen:H4
?CON-012Use a consistent elevation and shadow scaleminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Define 3–5 elevation steps in the design token system; replace arbitrary box-shadow values with the appropriate elevation token.
intent: Ad-hoc box-shadow values produce an inconsistent sense of depth — elements at the same logical level appear at different elevations, breaking the visual hierarchy.
css:C
?CON-013Apply a consistent corner-radius scale across componentsminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add border-radius tokens to the design system; audit components for hardcoded values and replace with the correct token.
intent: Components with mismatched border-radius values (some sharp, some rounded, some pill) appear cobbled together rather than designed as a system.
css:C
?CON-014Use consistent capitalization style for all UI textminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Choose one capitalization style (sentence case is recommended for readability) and document it; audit and standardize existing copy.
intent: Mixing Title Case, sentence case, and ALL CAPS for labels and headings looks accidental and undermines the sense of craft in the product.
nielsen:H4
?CON-019Use consistent motion tokens for animations and transitionsminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add motion tokens (duration-100, duration-300, ease-standard, ease-spring) to the design system; replace hardcoded transition values with the appropriate tokens.
intent: Components with mismatched animation durations and easing curves feel inconsistent — some snappy, some sluggish — undermining the sense of a coherent product.
css:C
?CON-022Apply consistent link styling and hover behaviorminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Define global anchor styles in the design system (color, text-decoration, :hover state) and avoid per-component overrides; use the Link component consistently.
intent: Links that look like buttons on one page and underlined text on another, or that have different hover colors, confuse users about what is clickable.
nielsen:H4
?CON-023Size iconography consistently throughout the UIminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Define icon size tokens and pass them via a size prop on the Icon component; avoid arbitrary width/height overrides on individual icon usages.
intent: Icons rendered at arbitrary sizes (some 16 px, some 18 px, some 24 px on the same page) look misaligned and unprofessional.
nielsen:H4
?CPY-006UI text uses sentence case, not title case or all-capsminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Convert title-case and all-caps UI strings to sentence case and enforce through a linting rule or style guide.
intent: Title case every word slows reading speed; all-caps impedes legibility. Sentence case (capitalise only the first word and proper nouns) matches natural reading.
nielsen:H4
?CPY-007Microcopy is as short as the task allowsminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Edit each string to remove hedging phrases ('Please note that…'), redundant nouns, and throat-clearing introductions.
intent: Wordy labels and instructions slow task completion and clutter the visual hierarchy. Every word should earn its place.
nielsen:H8
?CPY-009Success messages confirm what happened, not just 'Done'minornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Replace generic success copy with a specific statement of what changed and (where relevant) what happens next.
intent: Generic confirmations ('Done!') don't reassure users about what state change actually occurred.
nielsen:H1
?CPY-018Tone is consistent and aligned with the brand voiceminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Establish a tone-of-voice guide and audit copy for register mismatches; standardise to one voice level.
intent: Tonal inconsistency — formal in one screen, casual in another — erodes brand trust and creates a disjointed experience.
nielsen:H4
?FBK-005Buttons and links show an active/pressed stateminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add :active CSS styles (scale, colour change, or shadow removal) to buttons and links.
intent: An active state gives immediate tactile-like feedback confirming the interaction was registered, reducing double-taps and uncertainty.
nielsen:H1platform:pointer-affordance
?FBK-011Clickable non-button elements use the pointer cursorminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add cursor:pointer to every clickable non-native element and ensure native <button>/<a> elements don't override it away.
intent: The CSS pointer cursor is a system-level affordance for clickability. Custom click targets that use default cursor confuse pointer users.
platform:pointer-affordance
?FBK-018Copy-to-clipboard actions confirm successminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. After a successful clipboard write, briefly change the copy button icon/label or show a toast ('Copied!').
intent: A silent clipboard write gives users no evidence it worked, leading to frustrated repeated attempts or blind pastes.
nielsen:H1
?FBK-019Inline validation runs in real time where it aids completionminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Provide inline validation messages on blur (or live for confirmatory feedback) instead of deferring all errors to submit.
intent: Validating only on submit forces users to re-locate errors; real-time feedback on fields like email or password strength speeds correct completion.
nielsen:H9
?FBK-021Hover does not hide information the user may still needminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Position tooltips so they don't overlap the element that triggered them; use offset or directional placement logic.
intent: If a tooltip or popover appears on hover but its trigger hides the underlying information the user is consulting, comprehension is interrupted.
wcag:1.4.13
?FRM-003Give every button an explicit typeminorstatic
1 <button> without an explicit type (defaults to submit inside a form — a common accidental-submit bug).
Fix. Add type="button" to buttons that don't submit, and type="submit" to those that do.
evidence: index.html:14:5
intent: A <button> without a type defaults to type=submit inside a form, accidentally submitting on Enter or click.
html:H32
?FRM-006Mobile-optimised keyboard is specified with inputmodeminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add inputmode="numeric", "decimal", or "tel" to inputs that collect numbers or phone data.
intent: Without inputmode, numeric or phone inputs on mobile show a full QWERTY keyboard, slowing and frustrating entry.
wcag:1.3.5
?FRM-014Password fields offer a show/hide toggleminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add a labelled button beside the password input that sets type to 'text' and back to 'password'.
intent: Users who must enter a strong password with no visibility feedback make more errors; show-password lowers cognitive load.
wcag:3.3.2
?FRM-015Format hints appear before the control, not afterminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Position format hint text adjacent to or below the label, above the input field.
intent: Hints placed below an input are often missed before users start typing, leading to formatting errors that require correction.
wcag:3.3.2
?FRM-024Form fields use sensible default values where appropriateminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Pre-populate fields whose default can be reasonably inferred (locale, past submission, authenticated profile data).
intent: Leaving all fields blank forces users to remember and type values that could be reasonably pre-filled, increasing effort for everyone.
nielsen:H6
?I18N-016Units of measurement adapt to the user's localeminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Detect the user's measurement preference from locale and convert units accordingly; expose a manual override in settings.
intent: Distance in miles, weight in pounds, and temperature in Fahrenheit are unfamiliar to most of the world and create comprehension errors.
nielsen:H2
?I18N-018Icons and colours avoid culture-specific connotationsminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Audit iconography and colour semantics against CLDR cultural data and user research; prefer universally understood symbols or text labels.
intent: A thumbs-up is offensive in some cultures; red means danger in the West but luck in China — culturally specific choices alienate global users.
nielsen:H4
?MOT-002Animation is purposeful and communicates feedback or continuityminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Audit each animation and remove or replace ones that do not communicate information or reinforce spatial transitions.
intent: Decorative animation without purpose adds visual noise; every animation should inform the user about a state change or spatial relationship.
platform:material-motion-principles
?MOT-004Use natural easing curves instead of linear timingminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Replace linear with ease-out for elements entering the screen and ease-in for exiting ones.
intent: Linear animation feels mechanical; physics-based easing (ease-out for entrances, ease-in for exits) mirrors natural motion and feels polished.
platform:material-motion-easing
?MOT-011Overlay panels animate on enter and exitminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add a short (200ms ease-out) enter and exit transition to all overlay components.
intent: Abrupt appearance and disappearance of modals, drawers, and tooltips is disorienting; subtle enter/exit transitions provide spatial context.
platform:material-motion-container-transform
?MOT-012Micro-interactions are subtle and do not distractminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Limit micro-interaction travel distance and duration; prefer opacity or subtle scale (1 → 1.05) over large movement.
intent: Over-animated micro-interactions (large bounces, long ripples) draw attention away from content and feel toy-like.
platform:material-motion-principles
?MOT-016Hover transitions are quick (≤150ms)minornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Set transition durations on hover states to 100–150ms for a crisp, responsive feel.
intent: Slow hover transitions make the UI feel unresponsive; hover feedback should be near-instant.
platform:material-motion-speed
?MOT-017Focus indicator transitions are visible without being distractingminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Keep focus ring transitions at ≤100ms; prefer a fade-in over a flash or colour-cycling ring.
intent: A focus ring that appears or disappears too slowly is hard to track; one that flashes distracts sighted users.
wcag:2.4.11
?MOT-018List entry stagger is modest and not unnecessarily longminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Cap per-item stagger at 50–80ms and the list size that animates at 8–10 items to keep total duration short.
intent: Long staggered list animations make users wait for content that is already loaded; total stagger should feel snappy.
platform:material-motion-choreography
?MOT-019Loading spinners appear only after a short delayminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add a 200–300ms CSS animation-delay (or JS timeout) before a spinner becomes visible.
intent: A spinner that flashes on sub-100ms loads creates unnecessary visual noise; delaying it by 200–300ms avoids flicker.
platform:material-progress-indicators
?MOT-020Centralise animation values in motion tokensminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Define --duration-fast, --duration-normal, --ease-standard, etc. as tokens and replace inline values throughout.
intent: Scattered hard-coded durations and easing curves are hard to update consistently and lead to drift across components.
css:custom-properties
?NAV-001Provide a meta description for the pageminorstatic
1 document(s) without a meta description — affects search/social snippets.
Fix. Add a descriptive, unique <meta name='description'> to each page's <head>, summarising the page's content for search previews.
evidence: index.html:1
intent: A concise meta description helps search engines surface the page and gives users a preview before they visit.
html:G1
?NAV-011A footer provides utility navigation linksminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add a <footer> with a <nav aria-label='Footer'> containing links to key utility pages.
intent: Footer links (Contact, Privacy, Terms, Sitemap) are expected locations for utility content; their absence frustrates users who scroll to find them.
nielsen:H6
?NAV-022The home page is reachable by clicking the site logo or a Home linkminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Wrap the header logo in an <a href='/'> with aria-label='Home' or include visible 'Home' text as a navigation landmark.
intent: The logo-to-homepage convention is one of the web's strongest learned affordances; violating it disorientsusers.
nielsen:H1nielsen:H6
?PER-007Decorative images are hidden from the accessibility treeminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Mark decorative images with empty alt or move them to CSS backgrounds.
intent: Purely decorative images announced by AT add noise and slow comprehension for screen-reader users.
wcag:1.1.1
?PER-013Real text is used instead of images of textminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Replace images of text with styled HTML text wherever feasible.
intent: Images of text don't scale, recolor, or translate, and bloat the page.
wcag:1.4.5
?PER-018Captions identify speakers and significant soundsminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Author captions that include speaker IDs and important sound effects.
intent: Captions that omit who's speaking or key non-speech sounds lose meaning.
wcag:1.2.2
?PRF-002Lazy-load offscreen imagesminorstatic
1 <img> without a loading attribute — consider loading="lazy" for below-the-fold images.
Fix. Add loading="lazy" to all images that are not within the initial viewport.
evidence: index.html:10:5
intent: Loading images outside the initial viewport wastes bandwidth and delays LCP for visible content. Native lazy loading defers those requests until needed.
cwv:LCP
?PRF-004Remove stray console/debugger from shipped codeminorstatic
1 console.*/debugger statement(s) left in product code — noise in production and a minor info-leak.
Fix. Configure the bundler to strip console and debugger calls (e.g., Terser drop_console / drop_debugger) or use ESLint no-console.
evidence: ProductList.jsx:2:3
intent: console.log and debugger statements left in production code slow execution, clutter DevTools output, and may inadvertently leak sensitive data.
html:H
?PRF-023Prefetch likely next-navigation resourcesminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add <link rel="prefetch"> for predictable next routes on hover or use the framework router's prefetch prop to fetch them in the background.
intent: When the next user destination is predictable (e.g., clicking a product link), prefetching its document and assets makes the navigation feel instant.
cwv:TTFB
?PRF-025Subset and preload web fontsminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Use unicode-range in @font-face to load only the needed glyph subsets, and add <link rel="preload" as="font" crossorigin> for the primary weight.
intent: Full font files often include thousands of glyphs the UI never uses. Subsetting to the required Unicode ranges cuts font payload, improving FCP and LCP.
cwv:FCP
?ROB-020Documents have a valid DOCTYPEminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Ensure the HTML5 DOCTYPE <!DOCTYPE html> is the very first line of every HTML document.
intent: Without <!DOCTYPE html>, browsers enter quirks mode, causing parsing inconsistencies that break accessibility tree construction.
html:doctypewcag:4.1.1
?RSP-003Avoid large fixed pixel widthsminorstatic
1 large fixed px width(s) (≥600px) — these overflow small viewports; prefer max-width, %, or fr units.
Fix. Replace fixed pixel widths with percentages, max-width, or fr units so elements shrink with the viewport.
evidence: styles.css:1:8
intent: Hard-coded widths wider than the viewport force horizontal scrolling and break fluid layouts on small screens.
css:responsive-design
?RSP-004Adopt mobile-first breakpointsminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Rewrite max-width breakpoints as min-width equivalents, starting from the smallest supported viewport.
intent: Mobile-first CSS adds complexity only as the viewport grows, producing leaner stylesheets that load faster on mobile.
css:responsive-design
?RSP-010Use fluid typography with clamp()minornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Replace stepped font-size overrides with clamp(min, preferred-vw, max) to achieve smooth fluid scaling.
intent: Hard-coded font sizes at each breakpoint require many media queries; clamp() scales text smoothly between bounds.
css:clamp
?RSP-011Choose breakpoints based on content, not device namesminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Audit existing breakpoints; move them to the nearest point where the content naturally needs to reflow.
intent: Device-named breakpoints (iphone, ipad) become stale; content-driven breakpoints remain valid as device landscape evolves.
css:responsive-design
?RSP-021Avoid 100vw causing overflow due to scrollbar widthminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Replace width: 100vw on full-bleed sections with margin-inline: calc(-1 * var(--page-padding)) or a CSS grid breakout.
intent: On desktop, 100vw includes the scrollbar width while 100% does not; a 100vw element can cause a subtle horizontal scroll.
css:viewport-units
?RSP-023Place primary actions within thumb reach on mobileminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Move primary actions (CTA buttons, main nav) into a bottom bar or mid-screen position on small viewports.
intent: Actions at the top of a tall screen require an uncomfortable stretch; bottom-of-screen or mid-screen placement improves one-handed usability.
platform:apple-hig-thumb-reachability
?RSP-025Use fluid spacing alongside fluid typographyminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Define spacing tokens using clamp() to mirror the fluid scaling applied to type, keeping padding proportional to font size.
intent: Spacing that is fixed while typography scales fluidly creates proportion mismatches; both should scale together.
css:clamp
?SEM-011article and section carry accessible namesminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Provide a visible heading inside, or an aria-label, for every <article> and <section>.
intent: Unnamed <article> and <section> elements create unlabelled landmarks that can't be distinguished during landmark navigation.
wcag:1.3.1aria:region
?SEM-012Use strong and em for semantic emphasisminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Replace semantically significant bold/italic <span>s with <strong> or <em>.
intent: Bold/italic via <b>/<i> without meaning, or via CSS alone on a <span>, gives AT no cue that the text is emphasised.
wcag:1.3.1
?SEM-013Dates and times use the time elementminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Wrap dates in <time datetime="YYYY-MM-DD"> and times in <time datetime="HH:MM">.
intent: Machine-readable dates enable calendar apps, time-zone adjustment, and screen-reader disambiguation of ambiguous date formats.
wcag:1.3.1
?SEM-014Images with captions use figure and figcaptionminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Wrap captioned images in <figure><img …><figcaption>…</figcaption></figure>.
intent: An image followed by a caption paragraph has no semantic association unless wrapped in <figure>/<figcaption>.
wcag:1.3.1
?SEM-015Contact information uses the address elementminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Wrap contact information in an <address> element.
intent: Generic <p> or <div> contact blocks carry no semantic marker; <address> signals to AT and search engines that the content is contact info.
wcag:1.3.1
?SEM-018Term/definition pairs use dl, dt, and ddminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Refactor term/value blocks to <dl><dt>…</dt><dd>…</dd></dl>.
intent: Key-value or term/definition content expressed in plain paragraphs loses its relational semantics and AT cannot convey the pairing.
wcag:1.3.1
?SEM-020aside landmarks are used for tangential content onlyminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Move primary content out of <aside>; use <aside> only for sidebars, pull-quotes, and ads.
intent: Wrapping primary content in <aside> makes it sound supplementary to AT users and disrupts the page outline.
wcag:1.3.1aria:complementary
?SEM-022Quoted text uses the blockquote or q elementminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Wrap block quotations in <blockquote> and inline quotations in <q cite="…">.
intent: Quotations styled as blockquotes via CSS on a <div> carry no semantic weight; AT can't identify them as quotations.
wcag:1.3.1
?SPC-003Eliminate magic-number spacing valuesminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Replace magic numbers with design tokens or scale-step references (e.g. var(--spacing-3)).
intent: One-off values like 13px or 37px cannot be maintained consistently and signal an ad-hoc layout rather than a designed system.
css:custom-properties
?SPC-005Prefer gap over ad-hoc margins in flex/grid containersminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Replace margin hacks (first-child, last-child) on flex/grid children with a single gap declaration on the container.
intent: gap applies uniformly between items without collapsing or double-counting; ad-hoc sibling margins create fragile edge-case code.
css:gap
?SPC-009Apply consistent padding within a component familyminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Define padding as a token variant per component size (sm/md/lg) and use it everywhere that component appears.
intent: Buttons, cards, and badges within the same family should share padding proportions; discrepancies feel accidental.
nielsen:H4
?SPC-010Use a consistent border-radius scaleminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Define four or five border-radius tokens (e.g. --radius-sm: 4px, --radius-md: 8px) and replace one-off values.
intent: Mixing arbitrary radius values (3px, 6px, 9px, 12px) makes the UI feel inconsistent; a scale (none/sm/md/lg/full) provides coherence.
css:border-radius
?SPC-011Size interactive components consistently within a familyminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Define component size tokens (height, padding) per variant and apply them uniformly across the family.
intent: Height, min-width, and padding that varies arbitrarily across similar controls signals inconsistent implementation.
platform:material-component-specs
?SPC-016Keep icon-to-label spacing consistentminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Define an --icon-gap token (commonly 8px) and apply it uniformly with gap or margin-inline-start.
intent: Inconsistent gaps between icons and their adjacent labels look unpolished and imply different components.
platform:material-icon-button
?SPC-017Apply uniform padding inside cardsminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Extract card padding to a token (e.g. --card-padding: 16px) and apply it consistently via a shared class.
intent: Cards with inconsistent internal padding across the same page look like different components and break visual rhythm.
platform:material-card
?SPC-021Apply consistent list-item spacingminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Set a consistent gap on the list container instead of applying margin to individual items.
intent: Lists with varying row gaps look unfinished and make scanning harder when the eye can't rely on a stable rhythm.
css:gap
?STA-021Show stale-data or last-updated indicators when fresh data is unavailableminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Track the timestamp of the last successful fetch; when serving cached data after a failed refresh, display a 'Last updated' badge and a manual refresh button.
intent: Displaying cached data without labeling it as potentially stale can mislead users into acting on outdated information.
nielsen:H1
?TRU-006Do not disable autocomplete on useful fieldsminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Only disable autocomplete on fields where auto-fill is genuinely harmful (e.g. OTP entry); never use it globally.
intent: autocomplete=off prevents browsers and password managers from helping users, increasing friction and error rates on fields where assistance is beneficial.
html:autofill
?TRU-016Checkout and payment pages show trust signalsminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Place security badges or payment brand logos near the payment form to reassure users at the moment of highest anxiety.
intent: Security badges, recognised payment logos, and HTTPS indicators reduce cart abandonment by reassuring users before they enter payment details.
nielsen:H8
?TYP-004Headings use a tighter line-height than body textminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Override line-height on heading elements to a value between 1.1 and 1.25 to unify multi-line display.
intent: Large headings with the same 1.5 line-height as body text appear visually disconnected across lines; reducing it to ~1.1–1.3 keeps multi-line headings cohesive.
nielsen:H2
?TYP-005Limit the interface to 2–3 distinct font familiesminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Audit loaded font families via the network panel and remove any that duplicate the role of an existing typeface.
intent: Each additional typeface adds a network request, visual noise, and potential brand inconsistency; more than three families rarely improves communication.
nielsen:H4
?TYP-006Font sizes follow a modular type scaleminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Define a type scale (Major Third 1.25 or Perfect Fourth 1.333) as tokens and map every text style to the nearest step.
intent: Ad-hoc sizes (17 px, 22 px, 27 px) lack mathematical coherence; a ratio-based scale (1.25×, 1.333×) produces sizes that feel visually related and proportional.
nielsen:H4
?TYP-007Paragraphs have adequate space between themminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Apply margin-bottom: 1em to paragraphs or use a prose container utility that adds consistent paragraph spacing.
intent: Without sufficient inter-paragraph spacing, blocks of text appear as one undifferentiated mass, making scanning difficult.
nielsen:H2
?TYP-008Font stacks include system-font fallbacksminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Append a generic family and one or two system fonts (e.g. system-ui, -apple-system, Arial, sans-serif) to every font-family stack.
intent: If a web font fails to load, a fallback to a generic sans-serif family prevents invisible or mismatched text on slow connections.
css:font-family-generic
?TYP-010Uppercase text adds letter-spacing to restore legibilityminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add letter-spacing: 0.08em (or 0.05em–0.12em) to any element using text-transform: uppercase.
intent: All-caps text at normal tracking appears crowded because capitals are designed for sentence case; positive letter-spacing opens it up for readability.
nielsen:H2
?TYP-013Hyphens are enabled for long words in narrow containersminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Apply hyphens: auto and lang attributes to prose containers in narrow layouts; use overflow-wrap: break-word as a fallback.
intent: Without hyphenation, long technical terms or URLs overflow or force text to break awkwardly in narrow columns and at mobile widths.
css:hyphens
?TYP-014Numeric data in tables uses tabular figure variantsminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add font-variant-numeric: tabular-nums to table cells containing numeric data, or choose a typeface with tabular figures.
intent: Proportional numerals have varying widths so columns of numbers don't align; tabular (monospaced) figures keep decimals and digits in vertical columns.
css:font-variant-numeric
?TYP-015Avoid faux bold and faux italic renderingminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Load the specific font weight and style files used in the design rather than letting the browser synthesise bold/italic.
intent: When a bold or italic weight is unavailable, browsers synthesise it by slanting or fattening glyphs, producing poor-quality letterforms that erode visual polish.
css:font-synthesis
?TYP-021Weight contrast is used to create emphasis within body textminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Use font-weight: 600 for inline emphasis; reserve italics for citations or titles rather than general highlighting.
intent: Bold or semi-bold weight signals emphasis more accessibly than italics or color alone, and remains effective in greyscale and for color-blind users.
nielsen:H4
?TYP-022Layout avoids single-word orphan lines in proseminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Apply text-wrap: balance to headings and consider text-wrap: pretty (or orphans: 2) for prose paragraphs to prevent isolated words.
intent: An orphaned word at the end of a paragraph creates an awkward visual gap and a short, easily-missed line that disrupts reading rhythm.
css:text-wrap
?UND-003Mark foreign-language passages with a lang attributeminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Wrap foreign-language inline content in a <span lang="fr"> (or appropriate BCP 47 code).
intent: Inline phrases in a different language are mispronounced if the screen reader is not told to switch voice/pronunciation.
wcag:3.1.2html:H58
?UND-008Help mechanisms appear in a consistent locationminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Render help controls from a single shared layout component so their position never changes.
intent: Help links or chat widgets that move between pages are hard to find for users with cognitive or memory impairments.
wcag:3.2.6
?UND-015Content is written at an appropriate reading levelminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Simplify sentence structure, prefer common words, and break complex ideas into shorter paragraphs.
intent: Technical or unnecessarily complex prose excludes users with lower literacy, cognitive impairments, or non-native language skills.
wcag:3.1.5
?UND-016Unusual words and abbreviations are explainedminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Use <abbr title="..."> for abbreviations; link to a glossary or define technical terms inline.
intent: Jargon and unexplained acronyms are barriers for screen-reader users, non-native speakers, and people with cognitive disabilities.
wcag:3.1.3wcag:3.1.4
?UND-021Avoid jargon and internal terminology in user-facing copyminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Replace error codes and stack traces with friendly, action-oriented messages in the user interface.
intent: Exposing internal system names or technical codes in error messages and UI copy confuses non-technical users and erodes trust.
nielsen:H9wcag:3.1.5
?VIS-006Elements align to a consistent gridminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Use a 4- or 8-point grid system and enforce it via design tokens or layout utility classes.
intent: Arbitrary pixel offsets produce a restless, unpolished feel and obscure the intended reading order.
nielsen:H4
?VIS-010Whitespace separates distinct content sectionsminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Increase inter-section padding to a token two steps up from your base unit before adding borders or backgrounds.
intent: Generous space between sections allows the eye to rest and signals boundaries between topics without needing extra dividers.
nielsen:H2
?VIS-013Cards visually delineate grouped contentminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Apply a single card surface style from a design token so all content groups have uniform boundaries.
intent: Cards use surface contrast, shadow, or border to communicate that contained items belong together, reducing the need for users to infer grouping.
nielsen:H4
?VIS-014Dividers are used sparingly and only when space alone is insufficientminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Remove or reduce dividers and rely on generous whitespace to separate sections; add a subtle rule only where the eye still cannot find the boundary.
intent: Overuse of horizontal rules, borders, and separators creates visual noise that competes with actual content hierarchy.
nielsen:H8
?VIS-021Heading styles are consistent within each levelminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Define heading styles in a single design token or component and prohibit inline overrides on heading elements.
intent: Inconsistent H2 or H3 styling across a product signals randomness rather than system, eroding trust and confusing hierarchy.
nielsen:H4
?VIS-022Secondary metadata is visually de-emphasizedminornone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Apply a muted/secondary text token (lower contrast, smaller size, or lighter weight) consistently to all supplemental metadata.
intent: Timestamps, author names, tags, and other supplemental data shown at full body contrast compete with primary content for attention.
nielsen:H4
?CLR-018Avoid pure #000000 on #ffffff for body textinfonone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Replace pure #000000 on #ffffff with a slightly softened pair such as #1a1a1a on #fafafa that still exceeds 4.5:1.
intent: Maximum contrast (21:1) can cause halation and visual fatigue in sensitive users and on OLED screens; softened near-black on near-white is more comfortable at equivalent perceived clarity.
nielsen:H2
?FBK-010Optimistic UI updates for safe reversible actionsinfonone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Apply optimistic updates for reversible mutations; always handle the failure case by reverting and notifying the user.
intent: Showing the expected outcome immediately (before the server confirms) makes the UI feel fast without hiding risk, as long as the action can be reversed on failure.
nielsen:H1
?I18N-019Pseudo-localisation is used during development to catch i18n issues earlyinfonone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add a pseudo-locale to the development translation pipeline to surface truncation, missing externalisations, and layout issues automatically.
intent: Pseudo-locales (e.g. Ḟoo̊ ʙàr̃) reveal hard-coded strings, clipped containers, and bidi problems before any real translation begins.
nielsen:H4
?MOT-010Use skeleton screens instead of jarring spinners for content loadinginfonone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Introduce skeleton components that match the approximate shape of loading content for lists, cards, and articles.
intent: Skeleton placeholders maintain spatial context and reduce perceived wait time compared to a full-page spinner.
platform:material-progress-indicators
?NAV-016A sitemap or structured content index is availableinfonone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Generate and submit an XML sitemap; provide an HTML sitemap page linked from the footer for sites with complex information architectures.
intent: A sitemap gives users and search engines a complete view of the site's content, improving discoverability for both.
nielsen:H7
?ROB-017Redundant ARIA roles are not added to native elementsinfonone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Remove role attributes that merely restate the element's implicit role (role=button on <button>, etc.).
intent: Applying role=button to <button> or role=link to <a> is harmless but creates maintenance confusion and review noise.
aria:aria-in-html
?RSP-012Use container queries where component layout depends on parent sizeinfonone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Replace viewport-based media queries inside reusable components with @container queries.
intent: Viewport breakpoints cannot adapt a component embedded in a sidebar; container queries let components respond to their own available width.
css:container-queries
?RSP-024Test and verify layouts at 360, 768, 1024, and 1440px widthsinfonone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Add browser-based snapshot or visual regression tests at 360, 768, 1024, and 1440px viewport widths.
intent: Breakpoints only catch regressions if the design is verified at representative widths; gaps between breakpoints can hide layout bugs.
platform:google-device-metrics
?SPC-012Use negative space deliberately to balance layoutinfonone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Audit sections with less than 24px vertical padding and increase to the next scale step where appropriate.
intent: Cramped layouts increase cognitive load; generous, balanced white space makes content easier to scan and understand.
nielsen:H8
?TYP-016Web fonts are subsetted to reduce loading timeinfonone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Use the unicode-range descriptor in @font-face, or choose Google Fonts' automatic latin subset, to limit file size to characters actually used.
intent: Full-weight font files can exceed 400 KB per variant; subsetting to the character set in use (latin, latin-ext) significantly reduces transfer size.
css:unicode-range
?TYP-020Fluid headings use clamp() for smooth viewport scalinginfonone
No automated static check yet. Paste an API key in Settings to let the LLM judge it, or review manually.
Fix. Replace fixed breakpoint overrides on large headings with clamp(2rem, 4vw + 1rem, 3.5rem) or an equivalent fluid formula.
intent: Discrete breakpoint-based font sizes jump abruptly; clamp() produces a smooth interpolation between a minimum and maximum, maintaining proportion at every viewport width.
css:clamp

Passing & N/A — 13

Show 13 checkpoints
OPR-001Avoid positive tabindex valuesseriousstatic
No positive tabindex values.
intent: A tabindex greater than 0 overrides natural DOM order, creating an unpredictable focus sequence that confuses keyboard and AT users.
wcag:2.4.3
RSP-001Declare a responsive viewport metaseriousstatic
Documents declare a responsive viewport meta.
intent: Without a viewport meta tag, mobile browsers render the page at desktop width and scale it down, making content illegible.
html:viewport-meta
OPR-003Use links for navigation and buttons for actionsmoderatestatic
Links carry real href targets.
intent: Swapping the roles confuses AT users who expect Enter to follow a link and Space/Enter to activate a button, and breaks keyboard conventions.
wcag:4.1.2aria:button
ROB-002Keep element ids unique per documentmoderatestatic
No duplicate static id attributes within a file.
intent: Duplicate ids break labelledby/describedby associations and cause AT to announce wrong content for inputs.
wcag:4.1.1html:H93
CON-001Prefer classes/tokens over inline stylesminorstatic
No inline style attributes.
intent: Inline styles scatter visual decisions across the codebase, making design-system changes require hunting every usage. Class-based tokens keep the source of truth centralized.
css:C
ROB-003Avoid deprecated or obsolete elementsminorstatic
No deprecated/obsolete elements.
intent: Obsolete elements (<center>, <font>, <marquee>) may be unsupported by AT and are stripped from the accessibility tree in some browsers.
html:obsolete-elements
ROB-004Declare a character encodingminorstatic
Documents declare a character encoding.
intent: Without an explicit encoding declaration, browsers may mis-detect the encoding, corrupting characters and breaking AT parsing.
html:charset
SPC-001Avoid !important specificity warsminorstatic
0 !important declaration(s) — within tolerance.
intent: Cascading !important overrides indicate a broken specificity architecture and make spacing values impossible to override predictably.
css:cascade-specificity
PER-002Image submit buttons describe their actionseriousstatic
No <input type=image> elements.
intent: An <input type=image> is a button; without alt, its purpose ('Search', 'Submit') is unannounced.
wcag:1.1.1
FRM-002Point each label at its control via for/idmoderatestatic
No <label> elements.
intent: A visible label adjacent to a control but not programmatically linked is invisible to AT and doesn't expand the click target.
wcag:1.3.1html:H44
PER-003Inline SVGs are labelled or hidden from assistive techmoderatestatic
No inline <svg> elements.
intent: A meaningful inline SVG needs an accessible name; a decorative one needs aria-hidden. Neither default is safe to assume.
wcag:1.1.1aria:img
ROB-001Use only valid ARIA role valuesmoderatestatic
No explicit ARIA roles used.
intent: An unrecognised role is ignored by AT, so the widget conveys no semantic information to screen-reader users.
aria:roles
PRF-003Set font-display for web fontsminorstatic
No @font-face declarations.
intent: Without font-display, web fonts can block text rendering until fully downloaded, harming FCP and user-perceived speed. Using swap or optional avoids an invisible-text period.
cwv:FCP