Saksham Rule Set
Every rule Saksham checks — in the open
No black box. Below is the complete, current list of WCAG 2.1 Level A and AA success criteria Saksham evaluates on every scan, grouped by principle, with a plain-language note on exactly what "pass" means for each one.
Automated where it's reliable. Honest where it isn't.
Every criterion below is either fully automated and checked on every scan, or clearly flagged when human judgment is still required — like confirming live captions or reviewing genuinely borderline flash content. A rule engine you can't see the rules of isn't one you can trust, so we'd rather show you exactly where automation ends.
Perceivable 20
-
1.1.1
A
Non-text Content
All images, canvas, object/embed, image-map areas, and SVG icons have valid text alternatives or are correctly marked as decorative.
How Saksham checks this
- Audits <img> (missing/empty alt, empty-alt-inside-link), input[type="image"], and decorative-vs-informative <svg> (role, <title>, aria-label) both standalone and inside interactive controls.
- Flags icon-font glyphs (Font Awesome, Material Icons, Glyphicons) inside buttons/links that lack aria-hidden and a parent accessible name.
- Also checks <canvas>, <object>, <embed>, <area> image-map regions, and server-side ismap images for a text alternative.
- Flags generic/filename-style alt text (e.g. "image.png", "photo") via pattern matching, and skips anything marked aria-hidden="true" or role="presentation"/"none".
Applicable WCAG techniques
- H37 — Using the
altattribute on <img> elements. - H67 — Using
alt=""with notitleon images that assistive tech should ignore (decorative). - H86 — Providing a text alternative for ASCII art, emoticons, and leetspeak.
- ARIA6 / ARIA10 — Using
aria-label/aria-labelledbyas a text alternative for non-text content (icon-only controls, inline SVG). - F65 — Failure: omitting the
altattribute on <img>, <area>, orinput[type="image"]. - F30 / F39 — Failure: alt text that isn't a true equivalent (filenames, "image", "spacer") on informative or decorative images.
Reference patterns
- Informative image:
<img src="q3-chart.png" alt="Q3 revenue grew 18% year-over-year"> - Decorative image:
<img src="divider.png" alt="">— empty alt, never omit the attribute. - Icon-only button:
<button aria-label="Close dialog"><svg aria-hidden="true">…</svg></button> - Inline SVG icon (meaningful):
<svg role="img" aria-labelledby="t1"><title id="t1">Download</title>…</svg> - Complex image (chart/diagram): pair a short
altwith a visible caption oraria-describedbypointing at a full text/table equivalent nearby.
-
1.2.1
A
Audio-only and Video-only (Prerecorded)
Audio-only elements have a nearby transcript link; muted video-only elements have description tracks or text alternatives.
How Saksham checks this
- Checks every <audio> element's parent for a transcript link (href containing "transcript") or transcript-related text nearby.
- Checks muted <video> (video-only, no audio track) for a <track kind="descriptions"> or a nearby text alternative.
Applicable WCAG techniques
- G158 — Providing an alternative for time-based media for audio-only content (a full transcript).
- G159 — Providing an alternative for time-based media for video-only content (a transcript or audio track describing the visuals).
Reference patterns
- Audio-only:
<audio controls src="episode.mp3"></audio><a href="episode-transcript.html">Read the transcript</a> - Video-only (silent):
<video muted><track kind="descriptions" src="desc.vtt" srclang="en"></video>or an adjacent text description link.
-
1.2.2
A
Captions (Prerecorded)
Every detected video element includes a caption or subtitle track.
How Saksham checks this
- Scans every <video> for <track kind="captions"> or kind="subtitles">, exempting muted, controls-less video used purely as decorative background.
- Flags embedded YouTube/Vimeo/Wistia/Loom iframes (by src pattern) as an info-level "verify manually" item, since third-party captions can't be inspected from the host page's DOM.
Applicable WCAG techniques
- G87 — Providing closed captions.
- G93 — Providing open (burned-in) captions.
- F8 — Failure: captions that omit dialogue or important sound effects.
Reference patterns
- Native captions:
<video controls><track kind="captions" src="captions-en.vtt" srclang="en" label="English" default></video> - Third-party embeds: enable the platform's caption track (YouTube CC, Vimeo captions) and verify it manually — Saksham can only detect that the embed exists, not whether captions are turned on.
-
1.2.3
A
Needs manual check
Audio Description or Media Alternative (Prerecorded)
Cross-checked alongside the stricter 1.2.5 requirement — verify prerecorded synchronized media has an audio description or full text alternative.
How Saksham checks this
- Reuses the 1.2.5 audio-description-track detection rather than duplicating logic, since 1.2.5 (AA) is a strict superset of this Level A requirement.
- Raises a manual-verification note rather than a hard pass/fail, since confirming a "full text alternative" requires human judgment.
Applicable WCAG techniques
- G69 — Providing an alternative for time-based media (a full text description of the video).
- G78 — Providing a second, audio-described version of the prerecorded video.
- G173 — Accompanying non-interactive synchronized media with a text alternative.
Reference patterns
- Media alternative: a linked page describing every visual and auditory event in reading order — sufficient on its own without an audio-description track.
- Audio description track:
<video><track kind="descriptions" src="desc-en.vtt" srclang="en"></video>
-
1.2.4
AA
Needs manual check
Captions (Live)
Live streaming video is flagged for a manual caption check — live captioning quality can't be fully automated.
How Saksham checks this
- Live-caption accuracy and synchronization cannot be verified from the DOM, so this criterion is always routed to manual review whenever live-video markup is detected.
Applicable WCAG techniques
- G9 — Creating captions for live synchronized media (e.g. a CART/real-time captioning feed).
- G93 — Providing open captions for live broadcasts that don't support a closed-caption track.
Reference patterns
- Wire a real-time captioning/CART service into the stream, or use the broadcast platform's built-in live closed-caption feature — then manually confirm captions actually render during the stream.
-
1.2.5
AA
Audio Description (Prerecorded)
Every detected video element includes an audio description track.
How Saksham checks this
- Scans every <video> for a <track kind="descriptions"> element, exempting muted, controls-less video treated as decorative background.
Applicable WCAG techniques
- G78 — Providing a second, audio-described version of the prerecorded video.
- G173 — Accompanying non-interactive synchronized media with a text alternative as an equivalent to audio description.
Reference patterns
- Description track:
<video controls><track kind="descriptions" src="desc-en.vtt" srclang="en"></video> - Alternate described version: a second video file/link with extended audio description mixed into its soundtrack.
-
1.3.1
A
Info and Relationships
Form controls have accessible labels, tables have valid header/data associations, lists contain only valid children, heading hierarchy is valid, and landmark regions are present and uniquely named.
How Saksham checks this
- Labels: every input/select/textarea is checked for aria-label, aria-labelledby, title, a wrapping <label>, or <label for> — placeholder-only labelling is flagged separately from "no label at all".
- Tables: requires <th> header cells, a <caption> or aria-label, scope="col"/"row" on every <th>, at least one <td>, and headers="id1 id2" on data cells for two-dimensional "complex" tables (both column and row headers) per WCAG technique H43. Also flags multiple tables sharing one accessible name.
- Headings: detects level skips (e.g. h2 → h4) and empty headings.
- Grouped controls: 2+ same-name radio/checkbox siblings must sit inside <fieldset><legend>; existing fieldsets must have a non-empty legend.
- Lists: <li> must be inside <ul>/<ol>/<menu> or a role="list"-family container; <dt>/<dd> must be inside <dl>.
- Landmarks: requires exactly one <main>, a <header>/banner, <nav>, and <footer>/contentinfo; multiple <nav> landmarks must have distinct aria-label values; a capped "orphan region" walk flags visible text sitting outside every landmark. Landmark checks are skipped entirely inside third-party iframes.
Applicable WCAG techniques
- H44 — Using label elements to associate text labels with form controls.
- H65 — Using the title attribute when a visible <label> genuinely can't be used.
- H71 — Using fieldset and legend to describe a group of related radio/checkbox controls.
- H43 / H51 — Using id/headers (complex tables) or scope (simple tables), and proper table markup for tabular data.
- H42 / H48 — Using h1–h6 for real headings, and ul/ol/dl for real lists.
- ARIA11 — Using ARIA landmarks to identify page regions.
- F68 / F92 — Failure: a form control with no programmatic label; failure: role="presentation" stripping semantics from content that conveys structure.
Reference patterns
- Label:
<label for="email">Email</label><input id="email" type="email"> - Radio group:
<fieldset><legend>Plan</legend><input type="radio" name="plan">…</fieldset> - Complex table:
<th id="q1" scope="col">Q1</th>…<td headers="q1 row1">120</td> - Landmarks:
<header>…<nav aria-label="Primary">…<main>…<footer>…
-
1.3.2
A
Meaningful Sequence
No positive tabindex values that would disrupt the natural reading order.
How Saksham checks this
- Flags any tabindex value greater than 0, since positive values override the natural DOM tab order (the complementary "focusable-but-invisible" case is covered separately under 2.4.3 Focus Order).
Applicable WCAG techniques
- G57 — Ordering the content in a meaningful sequence that matches the DOM/reading order.
- F49 — Failure: a layout table or reordering hack that doesn't make sense when linearized.
Reference patterns
- Let natural DOM order define reading/tab order; use
tabindex="0"(not positive values) to add an element into the natural order, and CSS (flex/gridorder) only for pure visual reflow that keeps the same logical sequence for AT.
-
1.3.3
A
Sensory Characteristics
No instructions rely solely on shape, color, or position to identify UI components.
How Saksham checks this
- Regex-matches instructional text against a bank of shape/color/position-only patterns (e.g. "click the round button", "the link on the right", "the blue button").
Applicable WCAG techniques
- G96 — Providing textual identification of items that would otherwise rely only on sensory characteristics.
- F14 — Failure: identifying content only by its shape, size, or screen location.
Reference patterns
- Instead of: "Click the round button to continue." Use: "Click the Continue button."
- Instead of: "See the link on the right." Use: "See the Related articles link."
-
1.3.4
AA
Orientation
No CSS that locks the page to a single display orientation.
How Saksham checks this
- Parses every stylesheet for @media (orientation: portrait|landscape) rules that set display:none, visibility:hidden, or overflow:hidden specifically on html/body selectors — the concrete CSS pattern that locks a page to one orientation.
Applicable WCAG techniques
- No single dedicated technique number is commonly cited for this AA criterion — the WCAG Understanding doc's core guidance is simply: don't lock rendering to portrait or landscape via CSS orientation media queries or the
screen.orientation.lock()API, unless a specific orientation is essential (e.g. a piano app or check-deposit camera view).
Reference patterns
- Avoid:
@media (orientation: portrait) { body { display: none; } } - Instead: build a responsive layout that reflows in both orientations, and only call the orientation-lock API behind a clearly essential, user-facing reason.
-
1.3.5
AA
Identify Input Purpose
Personal-data input fields use valid, WHATWG-recognized autocomplete attributes.
How Saksham checks this
- Maps input[type] directly to autocomplete tokens (email/tel/url), and matches 18 personal-data regex patterns (name, address, cc-number, bday, etc.) against name/id/placeholder/aria-label/title.
- Validates any existing autocomplete value against the full WHATWG Autofill token list, including "shipping"/"billing"/"section-*" modifiers — not just checking that an attribute is present.
- Separately flags autocomplete="off" on a detected personal-data field, since it actively disables autofill and password-manager support.
Applicable WCAG techniques
- H98 — Using HTML 5.2 autocomplete attributes.
- F107 — Failure: an autocomplete value that doesn't match the actual purpose of the field.
Reference patterns
- Standard field:
<input type="email" name="email" autocomplete="email"> - Shipping modifier:
<input name="address" autocomplete="shipping street-address"> - Never pair a real personal-data field with
autocomplete="off"— use the correct token instead.
-
1.4.1
A
Use of Color
Links inside body text are distinguished from surrounding text by more than color alone.
How Saksham checks this
- Compares an inline link's computed color to its parent's, and only flags it when there is no other visual cue — underline, border-bottom, ≥200 font-weight difference, italic, background, or font-size difference.
- Scoped to links inside actual prose (p/li/td/dd) with real surrounding text, not standalone nav/menu items.
Applicable WCAG techniques
- G14 — Ensuring that information conveyed by color differences is also available in text.
- G182 — Ensuring additional visual cues are available when color alone is used to convey information.
- F73 — Failure: creating links that aren't visually distinguishable from surrounding text without relying on color.
Reference patterns
- Instead of color-only links:
a { color: #0a5c36; text-decoration: underline; }— underline (or a border/weight change) survives grayscale and color-blind rendering.
-
1.4.2
A
Audio Control
No audio or video auto-plays sound without native controls to pause or stop it.
How Saksham checks this
- Flags <audio autoplay> and unmuted <video autoplay> that lack the native controls attribute, since that removes the user's ability to pause/stop/mute independent of system volume.
Applicable WCAG techniques
- G60 — Playing a sound that turns off automatically within three seconds.
- G170 — Providing a control near the top of the page that turns off auto-playing sound.
- G171 — Playing sounds only on user request.
Reference patterns
- Safe default:
<video autoplay muted controls></video>— muted autoplay is fine; unmuted autoplay is not. - If sound must autoplay: always include
controlsso users can pause/mute immediately.
-
1.4.3
AA
Contrast (Minimum)
Every sampled text element — including placeholders, SVG text, and generated content — meets 4.5:1 (or 3:1 for large text).
How Saksham checks this
- Computes the WCAG relative-luminance/contrast-ratio formula against an "effective background" resolver that walks ancestors through transparency.
- Compounds CSS opacity across the entire ancestor chain (opacity doesn't inherit in the cascade but visually stacks) and alpha-blends semi-transparent text against its resolved background before measuring.
- Applies the correct large-text threshold (≥24px, or ≥18.67px at font-weight ≥700 → 3:1 required; otherwise 4.5:1).
- Separately checks ::placeholder rules, SVG <text>/<tspan> fill colors, input/textarea value text, and ::before/::after generated content (only when it contains ≥2 real alphanumeric characters, to skip icon-font glyphs).
- Also re-validates :hover/:active rules that change text color against that state's own background.
Applicable WCAG techniques
- G18 — Ensuring a contrast ratio of at least 4.5:1 for normal text.
- G145 — Ensuring a contrast ratio of at least 3:1 for large-scale text.
- F24 — Failure: specifying a foreground color without a background color (or vice versa), leaving contrast to chance.
Reference patterns
- Large text = ≥24px, or ≥18.67px at font-weight ≥700 → 3:1 minimum.
- Everything else → 4.5:1 minimum, including placeholder text, SVG <text>, and ::before/::after generated content.
- Always pair a text-color declaration with an explicit background-color — never rely on an inherited or unknown background.
-
1.4.4
AA
Resize Text
Zoom isn't blocked, and no fixed-height containers clip text when it's resized to 200%.
How Saksham checks this
- Flags user-scalable=no or maximum-scale below 2 in the viewport meta tag, and text-size-adjust:none in any stylesheet.
- Flags elements with inline overflow:hidden plus a fixed, non-percentage height that would clip text once resized to 200%.
Applicable WCAG techniques
- G142 — Using a technology (the browser) that has a documented, commonly-available zoom feature.
- C28 — Specifying the size of text containers using em units, so containers grow with the text instead of clipping it.
Reference patterns
- Never disable zoom:
<meta name="viewport" content="width=device-width, initial-scale=1">— omituser-scalable=no/maximum-scale. - Prefer relative sizing:
.card { min-height: 8rem; }over a fixed px height combined withoverflow: hidden.
-
1.4.5
AA
Images of Text
No inline images in text content are suspected of being images of text.
How Saksham checks this
- Heuristic-only: flags PNG/SVG images with alt text ≥10 characters and a wide "banner" aspect ratio (width ÷ height ≥ 2.5) sitting inside text-flow elements — exempting logos, icons, photos, and JPEG/GIF/WEBP formats.
- Reported as an info-level manual-review item, never a hard failure.
Applicable WCAG techniques
- C22 — Using CSS to control the visual presentation of real text, instead of a raster image of text.
Reference patterns
- Instead of: a PNG banner reading "50% off today". Use: real text styled with
font-family/font-weight/color— selectable, zoomable, and re-themeable. - Logos and wordmarks that are genuinely part of a brand image are exempt.
-
1.4.10
AA
Reflow
No horizontal scrolling required — no fixed-width elements wider than 320px or wide unwrapped text blocks.
How Saksham checks this
- Compares document scrollWidth to clientWidth to detect existing horizontal scroll.
- Flags inline widths >320px, computed min-width >320px, white-space:nowrap text blocks wider than 320px, and position:fixed elements wider than the current viewport.
- Explicitly exempts media elements (img/video/canvas/svg/iframe) and known carousel-track internals (Slick/Owl/Swiper) that legitimately need JS-computed pixel widths.
Applicable WCAG techniques
- C32 — Using media queries and grid CSS to reflow columns instead of forcing horizontal scroll.
- C31 — Using CSS Flexbox to reflow content responsively.
Reference patterns
- Avoid:
.sidebar { width: 480px; }on a layout that must fit a 320px viewport. - Prefer:
.sidebar { width: min(480px, 100%); }or a responsive grid/flex layout with wrapping.
-
1.4.11
AA
Non-text Contrast
Form borders, buttons, custom widgets, and meaningful icons meet the required contrast against their background.
How Saksham checks this
- For every input/select/textarea and button, determines whether the border or the background is the "visual boundary" and checks it against both the exterior (page) and interior (control) background, requiring ≥3:1 on the worse side.
- Repeats the same boundary logic for custom role="checkbox"/"radio"/"switch" widgets, and checks meaningful (non-decorative) SVG icons using their fill or stroke color.
- Scans stylesheets for :hover/:active text-color changes and re-validates the WCAG text-contrast threshold specifically for that interactive state, since a color fine at rest can fail on hover.
Applicable WCAG techniques
- G207 — Ensuring that a contrast ratio of at least 3:1 is provided for icons that convey information.
- The same 3:1 principle extends to form control borders, custom widget boundaries, and state indicators (checked/focused/selected).
Reference patterns
- Input border:
input { border: 1px solid #5c6b66; }on a white background — verify the border color itself reaches 3:1, not just the text inside. - Custom checkbox: ensure the checked-state fill/border also reaches 3:1 against both the control's own background and the page background.
-
1.4.12
AA
Text Spacing
No fixed-height text containers would clip content when spacing is increased to the WCAG text-spacing values.
How Saksham checks this
- Flags elements combining overflow:hidden with an inline fixed (non-percentage) height or max-height — the CSS pattern that clips content once the WCAG text-spacing values (1.5× line-height, 2× paragraph spacing, 0.12em letter-spacing, 0.16em word-spacing) are applied.
Applicable WCAG techniques
- C35 — Allowing for text-spacing override without loss of content or functionality.
Reference patterns
- Avoid:
.card { height: 4rem; overflow: hidden; }around real text content. - Prefer:
.card { min-height: 4rem; }so the box grows instead of clipping when spacing increases.
-
1.4.13
AA
Content on Hover or Focus
No title-attribute tooltips on non-form elements that would be dismissible or hoverable only.
How Saksham checks this
- Flags any [title] attribute that adds substantive content beyond the visible text or aria-label, since native browser tooltips can't be hovered into, aren't dismissible, and aren't guaranteed to persist.
- Also flags known custom-tooltip data attributes (data-tooltip, data-tippy-content, Bootstrap's data-bs-original-title) and Bootstrap tooltip triggers lacking aria-describedby.
Applicable WCAG techniques
- SCR39 — Making content that appears on hover or focus hoverable, dismissible (Esc key), and persistent until dismissed or no longer relevant.
- F95 — Failure: content shown on hover that disappears before the pointer can reach it (not itself hoverable).
Reference patterns
- Instead of native title tooltips: a custom tooltip driven by
aria-describedby, dismissible with Escape, that stays open while hovered/focused. - Bootstrap tooltip trigger:
<button data-bs-toggle="tooltip" aria-describedby="tip1">…</button>
Operable 17
-
2.1.1
A
Keyboard
No clickable non-interactive elements, nested interactive controls, or scrollable regions lacking a keyboard focus mechanism.
How Saksham checks this
- Flags non-native elements with inline onclick/onmousedown handlers that have no tabindex, plus a broader cursor:pointer heuristic for divs/spans/list items styled as clickable outside ~20 recognized ARIA widget roles.
- Flags <a> without href lacking tabindex/role, and interactive elements nested inside other interactive elements (invalid HTML that breaks focus/activation).
- Flags scrollable regions with real overflow content that have neither a tabindex nor a focusable descendant, making arrow-key scrolling impossible.
Applicable WCAG techniques
- G202 — Ensuring keyboard control for all functionality.
- H91 — Using native HTML form controls and links, which are keyboard-operable by default.
- SCR20 — Using both keyboard and other device-specific event handlers (not mouse-only).
- F54 — Failure: relying only on mouse/pointer events with no keyboard-equivalent handler.
Reference patterns
- Instead of:
<div onclick="submit()">Submit</div>use:<button onclick="submit()">Submit</button>— native elements get focus, Enter/Space activation, and correct role for free. - Custom scrollable panel:
<div tabindex="0" role="region" aria-label="Code sample" style="overflow:auto">…</div>
-
2.1.2
A
No Keyboard Trap
No visible dialogs lack a keyboard-accessible close mechanism.
How Saksham checks this
- Finds open <dialog>/[role="dialog"]/[role="alertdialog"] elements containing focusable content, and checks for a close/dismiss control via aria-label, class/id naming, or Bootstrap's data-bs-dismiss pattern.
Applicable WCAG techniques
- G21 — Ensuring that users are not trapped in content (a documented way out via standard keyboard commands).
Reference patterns
- Modal dialog: trap focus within the dialog while open, but always provide a keyboard-reachable close button and an Escape-key handler that returns focus to the trigger.
<div role="dialog" aria-modal="true" aria-label="Settings">…<button aria-label="Close">×</button></div>
-
2.1.4
A
Character Key Shortcuts
No accesskey attributes that would create conflicting single-character shortcuts.
How Saksham checks this
- Flags every [accesskey] attribute present, since single-character shortcuts can conflict with assistive-technology key commands unless the page lets users remap or disable them.
Applicable WCAG techniques
- G217 — Providing a mechanism to allow users to remap or turn off single-character key shortcuts.
Reference patterns
- Prefer no bare-letter
accesskeyat all; if one is required, ship a settings toggle that lets the user disable or remap it before it can conflict with their screen reader's own single-letter navigation keys.
-
2.2.1
A
Timing Adjustable
No meta-refresh or inline-script patterns suggesting an unadjustable session timeout.
How Saksham checks this
- Flags <meta http-equiv="refresh"> with a positive timeout outright.
- Scans inline <script> text for setTimeout/setInterval combined with session/timeout/expire/logout language as an info-level "verify manually" signal.
Applicable WCAG techniques
- G133 — Providing a checkbox on the first page that lets a user turn off a time limit.
- G198 — Providing sufficient time by letting the user extend/adjust the session limit.
- SCR16 — Using a script to warn the user of an inactivity timeout in time to request more.
Reference patterns
- A dialog appearing ~20–30s before session expiry with “Stay signed in” / “Log out” buttons, reachable and dismissible by keyboard, that resets the timeout on “Stay signed in.”
-
2.2.2
A
Pause, Stop, Hide
No marquee/blink elements, autoplaying carousels, or infinite CSS animations without a pause control or reduced-motion support.
How Saksham checks this
- Flags <marquee> and <blink> outright as deprecated and uncontrollable, and autoplaying, unmuted <video> without controls.
- Parses stylesheets for animation-iteration-count:infinite with no accompanying @media (prefers-reduced-motion) rule anywhere on the page.
- Detects known carousel/slider markup (Bootstrap, Slick, Owl, Swiper) and checks for a pause/stop control.
Applicable WCAG techniques
- G4 — Allowing the content to be paused and restarted from where it was paused.
- G187 — Using a technology-level mechanism to pause auto-updating content (carousels, tickers).
- SCR33 — Using scripting to control blinking content and stop it within five seconds.
Reference patterns
- Carousel: a visible, keyboard-reachable pause/play button; honor
@media (prefers-reduced-motion: reduce)by disabling autoplay entirely for users who request it.
-
2.3.1
A
Needs manual check
Three Flashes or Below Threshold
Fast-cycling animations or GIFs that could flash 3+ times per second are flagged for a manual confirmation.
How Saksham checks this
- Parses stylesheets for animations with a duration under 333ms (≥3Hz) running infinitely.
- Flags every animated GIF (excluding tiny ≤4px tracking pixels) as an info-level manual-review item, since frame rate and flash pattern can't be measured from the DOM.
Applicable WCAG techniques
- G19 — Ensuring no component of the content flashes more than three times in any one-second period.
- G176 — Keeping the flashing area small enough even when the flash rate exceeds the threshold.
Reference patterns
- Cap any CSS
animation-durationthat repeats a high-contrast flash below 3 cycles/second, or keep the flashing region under the general-flash area threshold (~25% of a 10° visual field at typical viewing distance).
-
2.4.1
A
Bypass Blocks
A focusable skip-navigation link is present, targets a real element, and is reachable first in the tab order or via :focus.
How Saksham checks this
- Detects a skip link by text matching /skip/i or common href/class/id patterns, and separately recognizes a <nav>+<main> landmark pair as an equivalent bypass mechanism.
- If found, verifies the target id actually exists in the DOM, checks it is the first focusable element, and — if visually hidden — searches stylesheets for a matching :focus rule that restores visibility.
- Flags display:none skip links as a hard error, since display:none removes an element from the tab order entirely — it can never receive focus at all.
Applicable WCAG techniques
- G1 — Adding a skip link at the top of the page that jumps to the main content.
- G123 — Adding a link at the start of a block of repeated content to jump past it.
- ARIA11 — Using ARIA landmarks (an equivalent bypass mechanism to a skip link).
Reference patterns
<a href="#main" class="skip-link">Skip to main content</a>…<main id="main">…</main>- Visually-hidden-until-focused CSS:
.skip-link { position: absolute; top: -100%; } .skip-link:focus { top: 0; }— neverdisplay: none, which blocks focus entirely.
-
2.4.2
A
Page Titled
The page has a descriptive, non-generic <title>.
How Saksham checks this
- Flags a missing <title> outright, and separately flags generic titles like "untitled", "home", "index", or "new tab" via pattern matching.
Applicable WCAG techniques
- G88 — Providing a descriptive title for the page.
- H25 — Using the <title> element to identify a web page and describe its topic/purpose.
Reference patterns
- Instead of:
<title>Home</title>use:<title>Dashboard – Acme Analytics</title>, unique per page/route in an SPA.
-
2.4.3
A
Focus Order
No focusable elements are invisible while still in the tab order, and no CSS order property silently reorders focus away from the DOM sequence.
How Saksham checks this
- Flags natively-focusable or tabindex≥0 elements with computed opacity:0 or visibility:hidden — explicitly exempting the standard "skip link hidden until focused" pattern.
- Flags focusable flex/grid children with a non-zero CSS order property, since DOM tab order and CSS visual order can silently diverge.
Applicable WCAG techniques
- G59 — Placing interactive elements in an order that matches the meaning/relationships in the content.
- C27 — Making the DOM order match the visual order, rather than relying only on CSS to reposition content.
- F44 — Failure: using tabindex to force a tab order that no longer preserves meaning or operability.
Reference patterns
- Write markup in the order it should be read/focused; use
orderin flex/grid only for cosmetic reflow that doesn't change logical meaning, and never use it to "fix" a focus-order bug that's really a DOM-order bug.
-
2.4.4
A
Link Purpose (In Context)
Every link has an accessible name, and none rely on vague text like "click here".
How Saksham checks this
- Flags links with no accessible name at all, and separately checks link text (or aria-label/title) against a 30+ term deny-list of vague phrases ("click here", "read more", bare digits, etc.).
- Cross-references every link by its accessible name and flags identical text pointing to two or more different href values.
Applicable WCAG techniques
- G91 — Providing link text that describes the purpose of the link.
- H30 — Providing descriptive link text on <a> elements (not "click here"/"read more").
- H33 — Supplementing link text with a title attribute, when extra context genuinely helps.
Reference patterns
- Instead of:
<a href="/report.pdf">Click here</a>use:<a href="/report.pdf">Download the Q3 report (PDF)</a> - Two different destinations should never share identical visible link text — disambiguate with the surrounding heading/row context or unique text.
-
2.4.5
AA
Multiple Ways
At least one secondary way to find a page — search, breadcrumbs, sitemap, or related links — is present.
How Saksham checks this
- Looks for any one of: a search input/role="search", breadcrumb markup, a link containing "sitemap", or a "related content" region — only fails when none of the four are present anywhere on the page.
Applicable WCAG techniques
- G125 — Providing a list of links to all other pages (sitemap).
- G161 — Providing a search function to help users find content.
- G64 — Providing a table of contents.
Reference patterns
<form role="search"><input type="search" aria-label="Search"></form>or a breadcrumb trail (<nav aria-label="Breadcrumb">) is usually the cheapest way to satisfy this.
-
2.4.6
AA
Headings and Labels
The page has headings, exactly one h1, and every heading contains descriptive text.
How Saksham checks this
- Requires at least one heading and exactly one <h1>, and flags heading text that is purely numeric, a single character, or punctuation-only via pattern matching.
Applicable WCAG techniques
- G130 — Providing descriptive headings.
- G131 — Providing descriptive labels for form controls and other UI components.
Reference patterns
- Instead of:
<h2>01</h2>use:<h2>Order #10294 summary</h2> - Form field labels should name the data being requested, not just "Field 1"/"Input".
-
2.4.7
AA
Focus Visible
Every focusable element has a visible focus indicator — native or a compliant custom one — with sufficient (≥3:1) contrast.
How Saksham checks this
- Parses every stylesheet rule containing :focus/:focus-visible/:focus-within, classifying each as "provides" (sets box-shadow/outline/border/background/color/transform) or "suppresses" (sets outline:none), then matches each rule's base selector against every focusable element via el.matches().
- Recognizes the modern *:focus{outline:0} + specific :focus-visible provider pattern as compliant, and only flags elements where suppression applies but no providing rule — native or explicit — actually matches.
- Independently re-runs the WCAG contrast formula against each rule's parsed outline-color to confirm any declared focus ring still reaches 3:1 against its background.
Applicable WCAG techniques
- G149 — Using CSS to include a visible focus indicator on an interactive control.
- G165 — Using the platform's default focus indicator so it's never accidentally overridden.
- G195 — Using an author-supplied, highly visible focus indicator.
- F78 — Failure: styling that removes the visual focus indicator without replacing it.
Reference patterns
- Avoid:
*:focus { outline: none; }with nothing replacing it. - Prefer:
:focus-visible { outline: 3px solid #1a5cff; outline-offset: 2px; }— keeps keyboard focus rings while suppressing them only for mouse clicks.
-
2.5.1
A
Pointer Gestures
No gesture-only interactions, and no carousels or sliders lacking single-pointer controls.
How Saksham checks this
- Scans inline scripts for gesture-library signatures (Hammer.js, native gesturestart/gesturechange/gestureend events) and flags known carousel/slider markup lacking visible prev/next controls.
Applicable WCAG techniques
- G215 — Providing controls to accomplish path-based or multipoint gestures using a single point of contact.
Reference patterns
- Swipeable carousel: also ship visible, tappable prev/next buttons so a single tap performs the same action as the swipe gesture.
-
2.5.2
A
Pointer Cancellation
No custom controls trigger substantive actions on the pointer-down event instead of pointer-up.
How Saksham checks this
- Flags non-native elements with onmousedown/ontouchstart/onpointerdown handlers, while excluding handler code that looks like drag/scroll setup rather than an activating action.
Applicable WCAG techniques
- G210 — Ensuring that drag-and-drop actions (and other down-event actions) can be cancelled.
Reference patterns
- Instead of: firing the action on
pointerdown/mousedown. Use:click(which fires on up) or explicitly commit onpointerup, so a user can slide off to abort.
-
2.5.3
A
Label in Name
Every element with an aria-label has its visible text label included in the accessible name.
How Saksham checks this
- For every aria-labelled button/link, strips punctuation from both the aria-label and the element's own visible text (ignoring aria-hidden descendants) and requires the normalized aria-label to contain the normalized visible text.
Applicable WCAG techniques
- G208 — Including the text of the visible label as part of the accessible name.
- G211 — Matching the accessible name to the visible label, so speech-input users can activate a control by speaking what they see.
Reference patterns
- Instead of:
<button aria-label="Submit form">Send</button>use:<button aria-label="Send message">Send</button>— the visible word "Send" must appear inside the aria-label.
-
2.5.4
A
Motion Actuation
No device-motion or device-orientation event handlers are required to operate the page.
How Saksham checks this
- Scans inline scripts for DeviceMotionEvent/DeviceOrientationEvent/Accelerometer/Gyroscope API usage as a signal that a UI alternative and a disable option need manual verification.
Applicable WCAG techniques
- G213 — Providing conventional controls as an alternative to motion actuation for simple, single-tap-equivalent functionality.
Reference patterns
- "Shake to undo": also expose an on-screen “Undo” button, and provide a setting to disable motion-triggered actions (useful for users with tremor or a mounted/fixed device).
Understandable 7
-
3.1.1
A
Language of Page
The <html> element declares a valid, recognized language.
How Saksham checks this
- Requires a non-empty lang attribute on <html> and validates it against a BCP 47 primary-subtag check — not just checking that the attribute is present.
Applicable WCAG techniques
- H57 — Using the lang attribute on the <html> element to identify the default human language of a page.
Reference patterns
<html lang="en">or<html lang="en-GB">— a valid BCP 47 subtag, not a display name like "English".
-
3.1.2
AA
Language of Parts
No elements have empty or invalid language attributes.
How Saksham checks this
- Applies the same BCP 47 validation used for 3.1.1 to every other [lang] attribute on the page, flagging empty or unrecognized values.
Applicable WCAG techniques
- H58 — Using language attributes to identify changes in the human language of a passage of text.
Reference patterns
<p>The French call it <span lang="fr">je ne sais quoi</span>.</p>
-
3.2.1
A
On Focus
No onfocus handler triggers an automatic navigation or context change.
How Saksham checks this
- Regex-scans onfocus handlers for context-change signatures: location=, window.open, .submit(), .navigate(.
Applicable WCAG techniques
- G107 — Using "activate" (a deliberate click/Enter/Space) rather than merely receiving focus as the trigger for a change of context.
Reference patterns
- Avoid:
input.addEventListener('focus', () => location.href = '/next') - Prefer: trigger navigation/submission on an explicit
click/change/submitevent a user chose to fire.
-
3.2.2
A
On Input
No select, radio, or checkbox onchange handler triggers automatic navigation or submission.
How Saksham checks this
- Regex-scans onchange handlers on select/radio/checkbox controls for the same context-change signatures used by 3.2.1.
Applicable WCAG techniques
- G80 — Providing a submit button to explicitly initiate a change of context.
- H32 — Providing submit buttons on every form that changes context on completion.
Reference patterns
- Avoid: a <select> that navigates on
onchangethe instant an option is picked. - Prefer: selecting an option updates state, and a visible
<button type="submit">Go</button>commits the change of context.
-
3.3.1
A
Error Identification
Required fields reference their error-message containers via aria-describedby.
How Saksham checks this
- Checks every [required]/[aria-required="true"] control for aria-describedby, escalating from an advisory note to a warning once aria-invalid="true" is already set (an active error state with no message wired up).
Applicable WCAG techniques
- G83 — Providing a text description identifying required fields that were not completed.
- G84 / G85 — Providing a text description when input isn't in the allowed list of values, or falls outside the required format/range.
- ARIA21 — Using aria-invalid to programmatically identify an error field.
Reference patterns
<input id="email" aria-invalid="true" aria-describedby="email-err"><p id="email-err">Enter a valid email address.</p>
-
3.3.2
A
Labels or Instructions
Required fields are visually marked, and no empty, orphaned, duplicate, or mismatched labels were found.
How Saksham checks this
- Checks the associated <label> text for a visible required indicator (*, "required") when aria-required isn't set.
- Separately flags empty <label> elements, <label for> pointing at a non-existent id, multiple labels pointing at the same control, title-only labelling, ARIA-label-only controls with no visible on-screen label, and aria-label text that doesn't contain the visible label text.
Applicable WCAG techniques
- G131 — Providing descriptive labels for all form controls.
- G89 — Providing expected data format and an example value.
- H90 — Indicating required form controls using <label> or <legend> text.
Reference patterns
<label for="phone">Phone number (required, format: 555-123-4567)</label>
-
3.3.3
AA
Error Suggestion
Inputs with pattern constraints explain the required format via title or aria-describedby.
How Saksham checks this
- Flags input[pattern] with neither aria-describedby nor title, and separately flags min/max/minlength/maxlength constraints with no accompanying hint element.
Applicable WCAG techniques
- G177 — Providing suggestions on how to correct an input error.
- G85 — Providing a text description when input falls outside the required format/range (reused here to describe the constraint up front).
Reference patterns
<input pattern="[0-9]{5}" aria-describedby="zip-hint"><p id="zip-hint">Enter a 5-digit ZIP code.</p>
Robust 3
-
4.1.1
A
Parsing
No duplicate id attributes anywhere in the DOM.
How Saksham checks this
- Counts every id in the DOM; for duplicates, cross-references whether the id is actively referenced by an aria-* relationship attribute, a <label for>, or belongs to a focusable element — since an ARIA-referenced duplicate silently breaks assistive-tech association while an unreferenced one is "merely" invalid HTML.
Applicable WCAG techniques
- H93 — Ensuring that id attributes are unique on a page.
- G134 — Validating web pages against HTML conformance rules.
Reference patterns
- Generate ids from a stable, unique key (record id, uuid) rather than a loop index that can collide across repeated components (e.g. two modals both rendering
id="title").
-
4.1.2
A
Name, Role, Value
Custom controls use valid ARIA roles and attributes, iframes are named, and regions have unique accessible names.
How Saksham checks this — the full ARIA sweep
- Accessible names: buttons, input[type=submit/button/reset] (including the value="" edge case that silently removes the native default name), custom [onclick] elements without a valid ARIA role, and combobox/meter/progressbar-role widgets.
- Reference integrity: every aria-labelledby/aria-describedby/aria-controls/aria-owns id is checked to resolve to an existing element in the DOM.
- State completeness: role="checkbox"/"switch" require aria-checked, role="tab" requires aria-selected, role="combobox" requires aria-expanded, role="slider"/"spinbutton" require aria-valuenow.
- Value validity: 18 enumerated ARIA attributes (aria-checked, aria-expanded, aria-current, aria-haspopup, aria-live, aria-sort, and more) are checked against their exact allowed token lists.
- Structural integrity: composite widgets need the correct parent (role="tab" inside role="tablist", role="option" inside role="listbox") and the correct children (role="list" needs role="listitem" descendants, and so on).
- Conflict detection: role="presentation"/"none" on a still-focusable element, and native form elements whose role attribute overrides their expected native semantics.
- Also covers: aria-haspopup on dropdown triggers, aria-hidden="true" on a still-focusable element, duplicate/unlabeled role="region" landmark names, unlabeled <iframe> elements, the deprecated role="directory", and a conservative disallowed-attribute-per-role matrix (e.g. aria-checked on role="button").
Applicable WCAG techniques
- G108 — Using markup features to expose name and role, allow user-settable properties, and notify of state changes.
- ARIA16 — Using aria-labelledby to provide a name for UI controls.
- ARIA5 — Using WAI-ARIA state/property attributes to expose the state of a UI component (aria-checked, aria-expanded, aria-selected).
- H91 — Using native form controls and links, which expose name/role/value for free.
Reference patterns
- Custom checkbox:
<div role="checkbox" tabindex="0" aria-checked="false" aria-labelledby="lbl">…</div>— togglearia-checkedon activation. - Custom tabs:
role="tablist">role="tab" aria-selected> matchingrole="tabpanel". - Prefer a native <button>/<input> over a custom
rolewhenever the native element already does the job.
-
4.1.3
AA
Status Messages
Toast, alert, and notification elements use a live-region role so assistive tech announces them without a focus change.
How Saksham checks this
- Matches a curated list of toast/alert/notification/snackbar/flash-message class-name patterns — deliberately excluding decorative icon elements and anything already nested inside a role="alert"/"status"/aria-live ancestor — and flags any match with neither a role nor an aria-live attribute of its own.
Applicable WCAG techniques
- ARIA19 — Using role="alert" or an assertive live region to identify errors without a change of context.
- ARIA22 — Using role="status" to present non-critical status messages politely.
Reference patterns
- Toast/success message:
<div role="status">Changes saved.</div> - Blocking validation error:
<div role="alert">Payment failed — check your card number.</div> - Insert the message element (or toggle its content) after page load — an aria-live region only announces changes that happen after it's already in the accessibility tree.
Ready?
See these rules run on your own pages
Create your free account and run your first scan in under a minute.