# EaseWeb > Accessibility remediation guides: what goes wrong, how to fix it, and how to verify the fix with a keyboard, screen readers, zoom and forced colors. Free to use, including as context for coding agents. The full outline, including topics still in progress, is at https://easeweb.dev/learn. MCP server (free, no login, Streamable HTTP): https://easeweb.dev/mcp. Tools: before_building (call before designing or building UI), find_fix, get_guide, test_steps, check_palette. Setup for Claude Code, Cursor and VS Code: https://easeweb.dev/services/developers ## Keyboard and focus - [Single-key shortcuts that can't be turned off](https://easeweb.dev/learn/character-key-shortcuts.md): Either give the shortcut a modifier, such as Ctrl + K and ⌘K, or give people a setting that turns the single keys off. Ignoring key presses in text fields is needed too, but on its own it passes nothing: the keys still act everywhere else. Test with: keyboard, speech input, screen reader. - [Dropdowns and fields that change the page on their own](https://easeweb.dev/learn/context-changes.md): Make the change happen when people ask for it. Either add a button that applies the choice or opens the calendar, or make the change in place: re-sort the list, or show the calendar, while focus stays where it is. Nothing that happens on focus may move focus, open a window or leave the page. Test with: keyboard, screen reader, zoom. - [Focus rings too thin or faint to spot](https://easeweb.dev/learn/focus-appearance.md): Make the indicator at least 2 CSS pixels thick and give its changed pixels 3:1 against what they replace. Either use a solid 2px ring coloured for each surface, a two-tone ring that works on any surface, or a fill change that swaps the button's colours on focus. Test with: contrast measurement, keyboard, zoom, forced colors. - [Floating chat buttons that cover part of keyboard focus](https://easeweb.dev/learn/focus-not-covered.md): Keep floating elements off the area the browser scrolls focus into. Either reserve the launcher's corner with scroll-padding-bottom, and pad the end of the content so the last row can scroll above it, or take the launcher out of the floating layer and put it in the page's header. Test with: keyboard, zoom. - [Sticky headers and banners that hide keyboard focus](https://easeweb.dev/learn/focus-not-obscured.md): Keep the bars from covering what the browser scrolls to. Either set scroll-padding on the page to the height of the sticky header and the fixed banner, so focus stops clear of them, or lay the page out so only the main content scrolls and the bars sit outside it. Test with: keyboard, zoom. - [Focus order that jumps around the page](https://easeweb.dev/learn/focus-order.md): Make the markup and the layout agree. Either write the markup in the order the design shows and remove the CSS that reverses it, or keep the markup and remove the reversal, so the layout follows the markup. Both pass, so choose by which order is right for the content, and never patch it with positive tabindex values. Test with: keyboard, screen reader, zoom. - [Keyboard traps in editors and embedded widgets](https://easeweb.dev/learn/keyboard-traps.md): Either stop intercepting Tab, so the browser moves focus as usual, or keep Tab for indenting and let Escape release the next Tab, with visible text that says so. Check third-party widgets the same way, because the trap is often in code you did not write. Test with: keyboard, screen reader. - [Div buttons that can't be reached with Tab](https://easeweb.dev/learn/native-buttons.md): Use a native button with a visible name and the correct type. Keep the existing visual design, add a visible focus state, and test the save result. Test with: keyboard, screen reader, zoom, forced colors. - [Missing skip links past repeated navigation](https://easeweb.dev/learn/skip-link.md): Give people a way past the repeated header. Either add a skip link as the first focusable element, shown on focus, that moves focus to the main element, or mark the page up with header, nav and main landmarks and a heading at the start of the content. Both pass; only the skip link helps sighted keyboard users, so most sites should have both. Test with: keyboard, screen reader, zoom. - [Missing focus indicators on custom buttons](https://easeweb.dev/learn/visible-focus.md): Replace the removed outline with a visible indicator: a ring coloured for each surface, or one two-tone ring that works on all of them. Check differing surfaces, clipping, zoom, and forced colors. Keep native focus behavior. Test with: keyboard, screen reader, zoom, forced colors. ## Forms - [Form errors that don't say how to fix them](https://easeweb.dev/learn/accessible-form-errors.md): Describe the error in words, associate it with the field, and provide a predictable route to the error. Keep entered values where appropriate and validate on the server. Test with: keyboard, screen reader, zoom, forced colors. - [Form fields that block or break autofill](https://easeweb.dev/learn/autocomplete.md): Give every field that asks for the user's own details the standard autocomplete token for its purpose, such as name, email, postal-code and tel. Remove autocomplete="off" from those fields and from the form, and replace made-up values with tokens from the HTML list. Test with: autofill, password manager, touch. - [Checkouts that take payment with no review or undo](https://easeweb.dev/learn/error-prevention.md): Either add a step that reads the booking back with a Change link for each choice before a "Confirm and pay" button, or state a time within which the booking can be cancelled for a full refund and put a Cancel button on the confirmation. A required "I've checked" box beside a summary also passes, but leaves people who tick it by habit paying for the slip. Test with: keyboard, screen reader, zoom, touch. - [Error summaries that don't say which fields failed](https://easeweb.dev/learn/error-summary.md): Name every field in error, and what to change, in text. Either show an error summary before the form that lists each problem as a link to its field, moves focus to itself and is backed by a message at each field; or list the problems in the existing banner; or put a message at each field and move focus to the first one. Test with: keyboard, screen reader, zoom. - [Radio buttons and checkboxes without a group name](https://easeweb.dev/learn/field-groups.md): Tie each question to its options in the markup. Either wrap the options in a fieldset and make the question its legend, or keep the existing wrapper and give it role="radiogroup" or role="group" with aria-labelledby pointing at the question. Keep a label on every option as well. Test with: keyboard, screen reader, zoom, forced colors. - [Form fields with missing or mismatched labels](https://easeweb.dev/learn/form-labels.md): Give every field a visible label that stays on screen and is also its accessible name. Either turn the existing text into a label element tied to the field with for and id, or keep the text where it is and point the field at it with aria-labelledby. Remove any aria-label that says something different from what is shown. Test with: keyboard, screen reader, zoom, forced colors. - [Checkout forms that ask for the same address twice](https://easeweb.dev/learn/redundant-entry.md): Either add a "Billing address is the same as delivery address" checkbox, ticked by default, that hides the billing fields, or fill the billing fields in with the delivery address so people change only what differs. Showing the delivery address on the same page to copy also passes 3.3.7, but leaves people copying it line by line. Test with: keyboard, screen reader, touch. - [Required fields marked only by colour](https://easeweb.dev/learn/required-fields.md): Say in visible text which fields are required, and add the required attribute to the same fields. Write "(required)" in each required field's label, or mark the optional fields "(optional)" with a sentence before the form saying the rest are required. Or mark the required fields with an asterisk and explain it in a line before the first field. Test with: keyboard, screen reader, zoom. ## Dialogs and overlays - [Confirmation dialogs that don't read out their question](https://easeweb.dev/learn/confirmation-dialogs.md): Either open a native dialog with role="alertdialog" and showModal(), with autofocus on Cancel, or give a custom alertdialog the same name, description and starting focus by hand. In both, once the item is gone, focus moves to something that is still on the page. Test with: keyboard, screen reader, zoom, forced colors. - [Cookie banners that keyboard users reach last](https://easeweb.dev/learn/cookie-banners.md): Put the banner where people meet it first. Either place it at the start of the page, before the header and in the page flow, so Tab and a screen reader reach it first, or open it as a native modal dialog with showModal() that Escape closes. Reserving the banner's height with scroll-padding keeps focus visible, but on its own it still leaves the banner last. Test with: keyboard, screen reader, zoom. - [Tooltips that vanish on approach and can't be dismissed](https://easeweb.dev/learn/hover-content.md): Either keep a hover tooltip open while the pointer is on the trigger or the pop-up text, hide it on Escape, and drop any timer. Or turn it into a toggletip: a button that opens the pop-up text as a native popover on click, which the browser closes on Escape, and copies the text into a status region so screen readers read it when it opens. Test with: mouse, keyboard, zoom, screen reader. - [Modals that leave keyboard focus behind them](https://easeweb.dev/learn/modal-focus-management.md): Open a native dialog with showModal(), or, when that is not possible, a custom dialog that makes the rest of the page inert. Give it an accessible name, choose suitable initial focus, and verify focus restoration. Test with: keyboard, screen reader, zoom, forced colors. - [Search panels that close on Tab and drop keyboard focus](https://easeweb.dev/learn/panel-dismissal.md): Either script the panel as a disclosure: aria-expanded on its button, Escape and a close button that return focus to it, and close on focus-out only when focus has left the whole component. Or make the panel a native popover, which gives you Escape, the expanded state and focus return without script. Test with: keyboard, screen reader. - [Toasts that vanish before you can read or undo them](https://easeweb.dev/learn/toast-timing.md): Don't put an action people may need behind a timer. Either keep the toast until the person closes it, or put the Undo where the deleted item was, with focus on it. Test with: screen reader, keyboard, zoom. ## Menus and navigation - [Help links that move from page to page](https://easeweb.dev/learn/consistent-help.md): Keep help at the same point in every page's order. Either keep the same Help link in the checkout's slimmed-down header, where every other page has it, or move help into a footer that every page shares, so it comes after the main content everywhere. The footer passes, but puts help at the end of every page. Test with: screen reader, keyboard, zoom. - [Navigation that marks the current page only by its look](https://easeweb.dev/learn/current-page.md): Mark the current page's link in the code as well as on screen. Either add aria-current="page" to it and draw the visual style from that attribute, or, where the menu cannot take attributes, add visually hidden text such as "(current page)" inside the link. Test with: screen reader, keyboard, forced colors. - [Dropdown menus that open only on hover](https://easeweb.dev/learn/nav-menus.md): Either turn each top-level item into a button that opens its submenu, says whether it is open with aria-expanded, and closes on Escape and when focus leaves; or use details and summary, which open with the keyboard and expose their state with no script; or, as the smallest change, show the submenu on :focus-within as well as :hover. The last two pass 2.1.1 but each leaves a gap, so the button is the one to build. Test with: keyboard, screen reader, zoom, touch. ## Tabs, accordions and disclosures - [Accordions that don't expose their open state](https://easeweb.dev/learn/accordion-controls.md): Use details/summary for a simple disclosure, or a button inside a heading for a custom accordion. Synchronize expanded state and actual panel visibility. Test with: keyboard, screen reader, zoom, forced colors. - [Show and hide buttons that don't say whether they are open](https://easeweb.dev/learn/show-hide-buttons.md): Either add aria-expanded to the existing button, keep it in step with the panel and draw the chevron from it, or rebuild the toggle as details and summary, which report open and closed by themselves. Either way, name the button for what it shows and put the panel right after it. Test with: keyboard, screen reader, zoom, forced colors. - [Tabs that only work with a mouse](https://easeweb.dev/learn/tabs.md): Build the tabs pattern on buttons, by hand or with a tested tabs component: role="tab" in a role="tablist", aria-selected on the shown tab, one Tab stop, and the arrow keys, Home and End to move between tabs. Keep every panel in the page and hide the others with hidden. Test with: keyboard, screen reader, zoom, forced colors. ## Custom inputs - [Autocomplete fields with silent, mouse-only suggestions](https://easeweb.dev/learn/combobox.md): Either build the field as an ARIA combobox: role="combobox", aria-expanded and aria-controls on the input, a listbox of options, aria-activedescendant moved by the arrow keys while focus stays in the field, and the count line as a status region. Or, for a plain list of words, give the input a list attribute and a datalist, and let the browser handle the keys and announcements, knowing you can't style that list and some browser and screen reader pairs read it poorly. Test with: keyboard, screen reader, zoom, forced colors. - [Custom dropdowns that a keyboard can't open or read](https://easeweb.dev/learn/custom-select.md): Either use a native select with a label and style its closed box, which the browser makes reachable, operable and announced on every device. Or, when the options need content a select can't hold, build the ARIA select-only combobox: a focusable element with role="combobox", a name and aria-expanded, a listbox of options, aria-activedescendant moved by the arrow keys, and Enter, Space, Escape and Tab handled as a select handles them. Test with: keyboard, screen reader, zoom, forced colors. - [Range sliders that only move by dragging](https://easeweb.dev/learn/sliders.md): Either use two native range inputs, one for each end, or build each thumb as a role="slider" with its values, the arrow keys and a click on the track, or keep the drag slider for the mouse and add a number field for each end. Whichever you choose, name each end, and read the value with its unit. Test with: keyboard, screen reader, mouse, touch. - [Toggle switches that don't say whether they are on](https://easeweb.dev/learn/toggle-switches.md): Either style a native checkbox to look like a switch, or give the custom control role="switch" with aria-checked, or make it a toggle button with aria-pressed. Whichever you choose, keep the exposed state in step with what is drawn and keep the name fixed. Test with: keyboard, screen reader, forced colors, mouse. ## Drag and drop - [Right-click, long-press and swipe actions with no button](https://easeweb.dev/learn/context-menus.md): Keep the right-click, long press or swipe as a shortcut, and put the same actions on a control every row shows: either an Actions menu button that opens the same actions and moves through them with the arrow keys, or a button for each action on the row. Making the rows focusable so Shift+F10 opens the menu is not enough on its own: nothing shows it, and on a Mac no key opens it without a screen reader. Test with: keyboard, mouse, touch, screen reader, speech input. - [Drag-and-drop boards that only a mouse can use](https://easeweb.dev/learn/drag-and-drop.md): Keep the drag, and give every card a way to move that needs no drag: either a button for each column it can go to, or a Move to list with a Move button. Announce each move in a status region, and keep focus on the moved card. Keyboard dragging with the arrow keys helps keyboard users, but on its own still leaves out people who use a single pointer. Test with: keyboard, mouse, touch, screen reader. ## Complex widgets - [Carousels that keyboard and screen reader users can't use](https://easeweb.dev/learn/carousels.md): Build the arrows and dots as native buttons with names, mark the current dot with aria-current, and put the slides in a polite live region so each new slide is read out. Test with: keyboard, screen reader, forced colors. ## Buttons and links - [Disabled buttons that can't be found or say why](https://easeweb.dev/learn/disabled-controls.md): Either leave the button enabled and, when it is pressed too early, show what is missing beside its cause, or mark it aria-disabled="true" so it stays focusable and link a visible reason to it with aria-describedby. Native disabled with a visible reason passes 4.1.2 too, but Tab still skips the button and its reason. Test with: keyboard, screen reader, forced colors, zoom. - [Icon-only buttons with no accessible name](https://easeweb.dev/learn/icon-buttons.md): Give each icon button a name in words and hide the icon from assistive technology. Either put visually hidden text inside the button, or add an aria-label to it, or show a visible text label next to the icon. All three pass; hidden text and aria-label keep the design, and a visible label helps everyone. Test with: keyboard, screen reader, zoom, forced colors. - [Empty image links and vague “Read more” links](https://easeweb.dev/learn/link-text.md): Give every link a name that says where it goes. Either add the missing words to the links you have, as visually hidden text or with aria-labelledby, or make each heading the link and stretch its click area with a pseudo-element. Both pass; the first changes less, the second reads more cleanly. Test with: keyboard, screen reader, mouse. - [Buttons that act on press and can't be cancelled](https://easeweb.dev/learn/pointer-cancellation.md): Run the action on the click event of a native button, and delete the pointerdown handler. A click fires only when the press and the release both happen on the button, so sliding off cancels it, a scroll never triggers it, and Enter and Space work with no extra code. Test with: mouse, touch, keyboard, screen reader. - [Tap targets too small and too close together](https://easeweb.dev/learn/target-size.md): Give every target room. Either make the buttons at least 24 by 24 CSS pixels, or keep the 16-pixel look and extend each button's hit area with a pseudo-element, or keep them small and space them so their centres are at least 24 pixels apart. Test with: target measurement, touch, mouse, zoom. - [Toggle buttons that don't say whether they are pressed](https://easeweb.dev/learn/toggle-buttons.md): Either add aria-pressed to the button and draw the filled heart from it, or make the toggle a checkbox drawn as the heart, or swap the name between "Add to favorites" and "Remove from favorites". Use only one of these on a control: a name that changes alongside aria-pressed contradicts itself. Test with: keyboard, screen reader, forced colors, mouse. ## Images and media - [aria-label on divs and spans that screen readers skip](https://easeweb.dev/learn/aria-label-on-generic.md): Take the words off the element that can't hold them. Either give the element a role that can be named, such as role="img" for a group of stars, or put the words inside it as visually hidden text, or write them as visible text beside the graphic and hide the graphic with aria-hidden="true". Test with: screen reader. - [Videos without audio description of what they show](https://easeweb.dev/learn/audio-description.md): Either rewrite the narration so it says what the picture shows, or publish a described version with the descriptions in the pauses and offer it from a button beside the player. A transcript that includes what is shown meets Level A only. Test with: screen off, keyboard, screen reader. - [Autoplaying sound with no way to stop it](https://easeweb.dev/learn/autoplay-audio.md): Either let the video wait until someone presses Play, or let it start muted, with buttons to pause it and turn the sound on. Test with: keyboard, screen reader, autoplay settings. - [Decorative images read out as file names or repeated text](https://easeweb.dev/learn/decorative-images.md): Make every image that adds nothing to the text around it silent, and take it out of the tab order. Either give it alt="" and remove any tabindex, or, for ornaments fixed in the theme, draw them with CSS so there is no img to read at all; photos that only repeat a link's text still get alt="". Test with: screen reader, keyboard. - [Images of text instead of real HTML text](https://easeweb.dev/learn/images-of-text.md): Put the words and figures on the page as real text: the size chart as an HTML table and the shipping cut-off as a paragraph, styled with CSS, and with a web font if the design needs one. Keep images for photos, diagrams and logos. Test with: zoom, forced colors. - [Missing alt text on images that carry information](https://easeweb.dev/learn/informative-images.md): Give every image that tells something a text equivalent of what it tells. Either write that message in the image's alt attribute, or move the message into real text on the page and give the image alt="". Both pass; the first changes only an attribute, the second also lets the words resize, translate and be searched. Test with: screen reader. - [SVG icons with no name, or read out when they should be silent](https://easeweb.dev/learn/svg-icons.md): Decide for every inline SVG whether it carries meaning, then name it or hide it. Either name each meaningful icon with role="img" and an aria-label, or write its meaning as visible text beside it and hide the icon with aria-hidden="true". Decorative icons get aria-hidden="true" in both. Test with: screen reader, forced colors. - [Podcasts and silent videos with no transcript](https://easeweb.dev/learn/transcripts.md): Write out every word said in the audio clip, with who says it and the sounds that matter, and describe every step the silent video shows, with any numbers on screen. Either put that text on the page right under the player, or on a page of its own with a link to it right beside the player. Test with: sound off, screen off, screen reader. - [Videos without captions, or with unchecked automatic ones](https://easeweb.dev/learn/video-captions.md): Write the captions, or correct the automatic ones word for word, including sounds that matter. Then either add them as a captions track that viewers turn on in the player, or draw them into the picture so they always show. Test with: sound off, keyboard, caption settings. ## Colour and contrast - [High contrast mode hiding focus, icons and selections](https://easeweb.dev/learn/forced-colors.md): Either draw focus, state and icons with properties that a contrast theme keeps and recolours (an outline, a border, an icon in currentColor, a tick for the chosen option), or add a forced-colors block that redraws what the theme removes in system colours such as Highlight and ButtonText. Don't turn forced colours off with forced-color-adjust: none. Test with: forced colors, keyboard, contrast measurement. - [Links in body text marked only by colour](https://easeweb.dev/learn/links-in-text.md): Give links in running text a cue that is not colour. Either keep the underline on them, or make their colour at least 3:1 against the surrounding text and add an underline on hover and focus. The underline works for everyone; the colour difference passes 1.4.1 but leaves links found only by lightness until someone points at them. Test with: contrast measurement, mouse, keyboard, touch, forced colors. - [Low-contrast form fields, icons and focus rings](https://easeweb.dev/learn/non-text-contrast.md): Give each field an edge, and each icon control a colour, with at least 3:1 against what is next to it. Use a focus ring that keeps 3:1 on every surface, with a contrasting halo where one colour cannot. Set these as design tokens so they hold when the theme changes. Test with: contrast measurement, keyboard, zoom, forced colors. - [Instructions that rely on colour, shape or position](https://easeweb.dev/learn/sensory-instructions.md): Point to a control by its name, not by how it looks or where it sits. Either put the words on the button and quote them in the instruction, or keep the icon and add its accessible name to the instruction beside a description that stays true at every screen width. Drop position words that a responsive layout can make false. Test with: screen reader, speech input, zoom. - [Low-contrast text on brand colours, photos and hover states](https://easeweb.dev/learn/text-contrast.md): Use a darker shade of the brand colour for text and keep the bright one for decoration. Put text over images on a panel, a scrim or a darkened photo that holds the ratio over the lightest possible image. Make hover and focus states darker, not lighter, and measure them like the resting state. Test with: contrast measurement, mouse, keyboard, forced colors. ## Zoom, reflow and text spacing - [Pages locked to portrait or landscape](https://easeweb.dev/learn/orientation.md): Delete the orientation rules that hide the page, cover it with a message or turn it sideways, and let the layout follow the width instead: one column on a narrow screen, more when there is room. Where one orientation suits a part better, say so in a note people can dismiss, over a page that still works. Test with: touch, zoom, keyboard. - [Pages that scroll sideways on a narrow screen](https://easeweb.dev/learn/reflow.md): Let every column shrink to the width of its container. Either cap the grid's minimum column width with min(100%, 320px), or make the grid one column by default and add columns only from a breakpoint. Both pass on this page; the first holds wherever the grid is placed, the second only while the breakpoint matches the space the grid really gets. Test with: zoom, keyboard. - [Enlarged text cut off by fixed-height boxes](https://easeweb.dev/learn/resize-text.md): Let every box that holds text grow with it. Either replace the fixed height with a minimum height and drop overflow: hidden, or do that and also fold the links behind a menu button when they no longer fit on one row. Both pass; the first is two lines of CSS, the second keeps a sticky header short at the cost of a script. Test with: zoom, keyboard. - [Button labels cut off when people widen text spacing](https://easeweb.dev/learn/text-spacing.md): Let every button grow when its label grows, and don't lock spacing with inline !important. Either let the label wrap inside a button with a minimum height, or let the row of buttons wrap so each button takes the width its label needs. Both pass; the first keeps the buttons side by side, the second keeps labels on one line wherever they fit. Test with: text spacing. ## Tables and data - [Data tables with headers that are only bold text](https://easeweb.dev/learn/table-headers.md): Mark each header cell as a header. Either use th with scope="col" or scope="row", or, where a header covers only part of a row or column, give each td a headers attribute listing the ids of its th cells. Where the markup cannot be a table, use the full set of ARIA table roles. Test with: screen reader, keyboard, zoom. ## Live updates - [Loading spinners that screen readers never announce](https://easeweb.dev/learn/loading-states.md): Put the loading and finished messages in a live region that is on the page from the start. Either use role="status", which waits for the screen reader to finish speaking, or role="alert", which interrupts it and is best kept for urgent news. Announce the start, the result and any error. Test with: screen reader, keyboard. - [Single-page app route changes that are never announced](https://easeweb.dev/learn/route-changes.md): After every client-side route change, move focus on purpose. Either focus the new view's h1, given tabindex="-1", so the screen reader reads it and the next Tab goes into the new content, or reset focus to the top of the document, as a page load does, and write the new page's name into a status region that was there from the start. Test with: keyboard, screen reader, zoom. - [Search and filter results that update without a word](https://easeweb.dev/learn/search-results.md): Write the number of results into a status region that is on the page before the first search, once the results are in and typing has paused. Either give the visible count line role="status" itself, or, when the count is rebuilt along with the results, keep a visually hidden status region in the page layout and write the same words there. Don't put aria-live on the results list, and don't move focus to the results. Test with: screen reader, keyboard. - [Status messages that are shown but never announced](https://easeweb.dev/learn/status-messages.md): Write the message into a status region that is already on the page and empty. Either keep a short status line next to the thing it reports on, or keep the toasts but render them inside a toast container with role="status" that is there from page load. Don't move focus to the message. Test with: screen reader, keyboard, zoom. ## Time limits and motion - [Pulsing buttons, glowing badges and animations that never stop](https://easeweb.dev/learn/endless-animations.md): Give the animation an end: a few cycles that are over within five seconds, written inside the no-preference media query. Or take the animation away and let the button stand out by its size, colour and place, or move it only once, when someone points at it or tabs to it. A decoration needs no Pause button, because it doesn't need to keep moving at all. Test with: reduced motion, keyboard, mouse, zoom. - [Badges and icons that blink more than three times a second](https://easeweb.dev/learn/fast-blinking.md): Either keep the dot still and let the word "Live" carry the meaning, or slow the blink to a soft pulse of one flash every two seconds that stops after two. Test with: flash analysis, reduced motion. - [Flashing banners and videos that can trigger seizures](https://easeweb.dev/learn/flashing-content.md): Either stop the flashing and let the banner stand out by its colour and size, or slow it to one flash a second and let it end after three. Keeping the fast strobe to a small badge also passes 2.3.1, while the badge stays under a quarter of a 341 × 256 pixel field at every zoom; video and animated images are fixed in the edit, checked with a flash analysis tool. Test with: flash analysis, reduced motion, zoom. - [Tickers, carousels and animations that can't be paused](https://easeweb.dev/learn/moving-content.md): Let the messages stay still, side by side and wrapping on small screens; or show one at a time, changed only by Previous and Next buttons; or keep the scroll and put a Pause button before it that stops the movement and shows every message. Pausing on hover can stay as an extra, but it is never the way to stop it. Test with: keyboard, mouse, reduced motion, zoom, screen reader. - [Animations that ignore the reduced-motion setting](https://easeweb.dev/learn/reduced-motion.md): Turn off the motion that scrolling and other interaction trigger when people ask for less of it. Either honour the operating system's reduced-motion setting with prefers-reduced-motion in CSS and in scripts, or add a motion switch on the page whose default follows that same setting. Either way, the content still appears, just without moving. Test with: reduced motion, keyboard, mouse. ## Page structure - [Accessibility statements that are missing or say nothing](https://easeweb.dev/learn/accessibility-statement.md): Either publish an accessibility statement page, or add an accessibility section to the help page people already use, and link it from the footer of every page: the page under the word Accessibility, the help page under Help. Either way, say which standard the site was tested against and how well it meets it, list each known barrier with another way round it, give a way to reach you and a reply time, and date it. Test with: keyboard, screen reader, zoom. - [Scanned and untagged PDFs that screen readers can't read](https://easeweb.dev/learn/accessible-pdfs.md): Either publish the menu as an HTML page and link to that, keeping a PDF only as a print version that says the same, or replace the file with a tagged PDF exported from its source document with tags turned on, then checked. A scan with no source file has to be retyped, or run through OCR and tagged and checked by hand: OCR alone is not a fix. Test with: screen reader. - [aria-hidden content that keyboard focus still reaches](https://easeweb.dev/learn/aria-hidden-focus.md): Hide the closed menu from everyone at once, not from screen readers alone. Either hide it with CSS, visibility: hidden or the hidden attribute, while it is out of view, or make it inert, which takes it out of the tab order, the pointer and the accessibility tree. Keeping aria-hidden and giving every link inside tabindex="-1" also passes, but only while no control inside is missed. Test with: keyboard, screen reader. - [Iframes with a missing or generic title](https://easeweb.dev/learn/frame-titles.md): Give every visible iframe a title attribute that says what it holds, such as “Map of our studio”, in the language of the page, and replace generic titles that come with embed code. For a frame a third-party script inserts, set the title through the widget's own option, or after the frame is inserted, and check the rendered page. Test with: screen reader. - [Pinch, drag and tilt gestures with no button](https://easeweb.dev/learn/gestures.md): Keep the drag, the pinch and the tilt as shortcuts, and put the same actions on buttons beside the view: Turn left, Turn right, Zoom out and Zoom in, with the zoomed photo in a box the browser scrolls. Leave turning by tilt off until a switch on the page turns it on, and let the same switch turn it off. Arrow keys on a focused view are not enough on their own: they help keyboard users, but someone with a mouse or one finger still has to drag and pinch. Test with: mouse, touch, device motion, keyboard, speech input, screen reader. - [Headings chosen by size, not by structure](https://easeweb.dev/learn/heading-structure.md): Mark each section title as a heading at the level its place in the outline calls for, and set its size with a class. Either change the styled divs to h2 and h3 elements, or, where the element can't change, add role="heading" and aria-level to them. Either way, remove the empty heading and reword the one that says only “More”. Test with: screen reader. - [Navigation landmarks that all sound the same](https://easeweb.dev/learn/landmark-names.md): Give every landmark that appears more than once its own short name, without the role in it. Either add an aria-label to each nav, or name the ones that have a visible heading with aria-labelledby pointing at it and add an aria-label only to those without one. Test with: screen reader. - [Lists built from divs, icons and typed numbers](https://easeweb.dev/learn/list-markup.md): Mark every group of items that looks like a list as one. Either rebuild the features as a ul and the steps as an ol, letting the ol number them, or, where only attributes can change, give the existing container role="list" and each item role="listitem", keeping the typed numbers. Either way, keep the list markup valid: only items directly inside the list, and no role that overrides it. Test with: screen reader. - [Page language set wrong, so screen readers mispronounce it](https://easeweb.dev/learn/page-language.md): Set lang on the html element to the language each page is written in, and give any passage in another language its own lang. Either render the html lang from the page's locale in the server template, or, in an app that changes language in the browser, set document.documentElement.lang every time the locale changes. Both pass; which one applies depends on how the site switches language. Test with: screen reader. - [Page titles that are the same on every page](https://easeweb.dev/learn/page-title.md): Give every page a title that names it before the site name. Either set the title in each page's template, so the server sends it with the page, or, in a single-page app, set document.title in the router every time the view changes. Both pass; which one applies depends on how the site renders its pages. Test with: screen reader. - [Reading order that doesn't match the layout](https://easeweb.dev/learn/reading-order.md): Make the order in the code carry the meaning. Either write the source in the order it is read, each item's name before its amount with the row laid out by flexbox (justify-content: space-between) instead of a float, and each delivery option in one row of a table, or, where the markup can't move, add visually hidden words that tie each value to what it belongs to. Test with: screen reader, styles off. ## Optional - [For developers](https://easeweb.dev/services/developers): the guides as MCP tools in a coding agent, with the checks split into what the agent can confirm and what a person must. - [Services](https://easeweb.dev/services): manual testing, remediation and retests by a Section 508 Trusted Tester. - [Pricing](https://easeweb.dev/pricing): published starting prices for testing, fixes, retests and the Shopify store review. - [Vendor review](https://easeweb.dev/services/vendor-review): manual testing for sites that already pay for an accessibility widget, scanner or AI auto-fix, showing what still stops people. - [What automated checks miss](https://easeweb.dev/learn/automated-checks): what Lighthouse, axe, scanners, overlay widgets and AI testing can establish about accessibility, and what takes a person using the page. - [Shopify stores](https://easeweb.dev/services/shopify): a fixed-price review of a Shopify store's templates, with every barrier sorted by source: theme, installed app, or Shopify's checkout. - [For larger organizations](https://easeweb.dev/services/enterprise): independent accessibility testing for in-house teams, beside the vendor they already have: key journeys tested by hand, evidence and an ACR, fixes through their own review, and a guardrail in CI. - [Japan’s 2024 law change](https://easeweb.dev/japan): what the amended Act for Eliminating Discrimination against Persons with Disabilities requires of a website, and what it does not (reasonable accommodation, 環境の整備, JIS X 8341-3).