YMI Jeans · WCAG 2.2 A and AA · Phase 1
A source review of the Impulse 8.2.1 theme for ymijeans.com. No theme code was changed to produce this report.
The theme already has a skip link, a main landmark, translated names on several header icons, hidden decorative icons, a keyboard outline class, and assets/accessibility.css. Those pieces do not cover the custom collection UI, dialogs, or contrast overrides.
The largest barriers are on primary shopping paths:
Counts are distinct defects. The same defect in the default, Royalty, and YMIG headers is one issue.
Filter by severity or search by id, file, or wording. Each card is the same issue recorded in the markdown audit.
Why it matters. Keyboard and screen-reader users cannot change sort.
Smallest fix. Keep the native select as the visible control, or add arrow keys, aria-controls, and a roving tabindex.
Why it matters. Focus leaves an open cart, menu, search, or quick shop and reaches the page behind it.
Smallest fix. On Tab from the last control, prevent default and focus the first control.
Why it matters. Closing quick shop and other modals does not restore focus to the opener.
Smallest fix. Set aria-expanded, then assign activeSource to the element.
Why it matters. The hero promotion has no name. Text inside the image is unavailable.
Smallest fix. Add alt from a section setting. Name the link with that text. Hide the unused breakpoint image from assistive technology.
Why it matters. Assistive technology does not treat the cart, menu, search, or popups as dialogs.
Smallest fix. Add dialog semantics and an accessible name. Do not rewrite the components.
Why it matters. On Royalty or YMIG, cart and menu buttons point at the hidden default drawer.
Smallest fix. Use one header, or unique ids, and point aria-controls at the visible drawer.
Why it matters. Pages have several level-1 headings that are navigation, not the page title.
Smallest fix. Change the brand links to spans. Keep a single page h1.
Why it matters. Collection pages have no heading on mobile.
Smallest fix. Keep a visually hidden h1 so the mobile layout stays the same.
Why it matters. The mobile logo link has no accessible name.
Smallest fix. Use the same alt on the mobile image.
Why it matters. Empty Shopify alts expose product photos as decorative.
Smallest fix. Fall back to the product title. Do the same in the variant card.
Why it matters. The newsletter submit button has no name.
Smallest fix. Add a visually hidden label that is not display:none.
Why it matters. Screen readers announce an unlabeled button.
Smallest fix. Add the existing close translation as an aria-label. Hide the SVG.
Why it matters. Checking a filter can toggle the wrong control.
Smallest fix. Build a unique id from location, filter name, and index.
Why it matters. The price filter has no accessible name.
Smallest fix. Pass handle labels into noUiSlider.create.
Why it matters. Keyboard users cannot use those controls.
Smallest fix. Use button type="button". Give each clear link a unique id.
Why it matters. Option groups have no accessible name, and swatches are color-only.
Smallest fix. Use a clip technique for the legend and the color name. Scope radio names with the product id.
Why it matters. Size-chart and other tips are mouse-only.
Smallest fix. Use a button with aria-expanded, and restore focus on close.
Why it matters. Keyboard users cannot reopen the newsletter from the reminder.
Smallest fix. Make the message a button.
Why it matters. The disclosure is not keyboard operable and its state is not exposed.
Smallest fix. Use a button and set aria-expanded with the panel.
Why it matters. Moving video cannot be paused from the keyboard.
Smallest fix. One button that calls play or pause and updates its name.
Why it matters. Autoplay longer than 5 seconds needs a way to stop it.
Smallest fix. Add a pause button that calls pausePlayer and playPlayer.
Why it matters. Screen readers are not told when a submenu is open.
Smallest fix. Put aria-expanded on the link and toggle it.
Why it matters. Contrast is about 2.55:1. AA needs 4.5:1.
Smallest fix. Set footer text to at least #767676. #595959 is safer.
Why it matters. The field is invisible.
Smallest fix. Override the input and placeholder with the same dark footer text.
Why it matters. Contrast is about 1.98:1.
Smallest fix. Darken chip text to at least #767676.
Why it matters. A failed address save gives no error text.
Smallest fix. Output the errors and tie them to the fields with aria-describedby.
Why it matters. Sighted users cannot find or see the pause control.
Smallest fix. Show a pause button whenever autoplay is true.
Why it matters. Sighted users lose the field instruction.
Smallest fix. Keep a visible label. Leave the placeholder as a hint.
Why it matters. Screen readers are not pointed at the message for the field that failed.
Smallest fix. Point each invalid control at its error.
Why it matters. Users learn a field was required only after submit.
Smallest fix. Add required and the word required in the visible label.
Why it matters. Browsers cannot offer the user’s stored values.
Smallest fix. Add the matching autocomplete token. No visual change.
Why it matters. Speech input that says the visible label can miss the control.
Smallest fix. Remove the aria-label and keep the label element.
Why it matters. It is exposed as a link that cannot be used.
Smallest fix. Use a heading element, or restore a real href.
Why it matters. Collapsed categories do not expose state on first load.
Smallest fix. Set aria-expanded="false" on every category that is closed.
Why it matters. Contrast is about 3.62:1. The value beside it passes.
Smallest fix. Darken the prefix to at least #767676.
Why it matters. Computed contrast is about 1.79:1.
Smallest fix. Use dark text on a solid background that clears 4.5:1.
Why it matters. Black at 50% on white is about 3.95:1.
Smallest fix. Raise placeholder contrast, and add visible labels.
Why it matters. The banner link has no name.
Smallest fix. Add an accessible name from a text setting. Do not use href="#" with no destination.
Why it matters. Logo and press links can be empty.
Smallest fix. Fall back to a caption or a fixed name such as View on Instagram.
Why it matters. The button is announced as Close while it opens the gallery.
Smallest fix. Use a zoom label. Keep the SVG decorative.
Why it matters. Some pages have no h1.
Smallest fix. Show the h1 from the title setting alone.
Why it matters. Activating it submits the product form.
Smallest fix. Set type="button" if script handles checkout.
Why it matters. Before script runs, state is missing.
Smallest fix. Output aria-expanded from the collapsed setting.
Why it matters. Helper and error text fails AA, down to about 1.61:1.
Smallest fix. Use #595959 or darker for text that is meant to be read.
Why it matters. title is an unreliable accessible name.
Smallest fix. Add aria-label with the same words.
Why it matters. The purpose is unclear, and it is not a real link.
Smallest fix. Use a button named Show N more colors.
Why it matters. The first announcement is wrong.
Smallest fix. Initial label Pause video.
Why it matters. The button name is wrong on the YMIG header.
Smallest fix. Match the default header: aria-label without json.
Why it matters. Invalid ARIA is ignored.
Smallest fix. Use aria-expanded.
Why it matters. Unexpected navigation. WAVE flags this as a redirect.
Smallest fix. Redirect in Liquid if merchandising still wants that URL.
Why it matters. Translated storefronts mix languages.
Smallest fix. Use existing translation keys.
Why it matters. The symbol is unexplained.
Smallest fix. Add one line: Required fields are marked *.
Why it matters. Broken markup can drop or mis-bind the label.
Smallest fix. Remove the value from the tag.
Why it matters. Location in a set of pages is weaker than it should be.
Smallest fix. Add aria-current="page" and always include the current crumb.
Hero, mobile logo, product cards, cart promo, and logo tiles can ship without a name. Decorative hover images and social icons are already handled.
Header icon links for search, cart, and account already have names. New-window warnings are AAA and are not in this count.
Quantity buttons are real buttons with names. Newsletter and video-modal close buttons are labeled.
Login, register, and address fields have matching labels. The gaps are sort, filters, errors, required state, and autocomplete.
Product, search, cart, and 404 each have a page h1. Extra brand h1s sit beside them.
Skip link, one main, and one footer are in place. Three headers share the same ids until script shows one.
Desktop menus open on focus. The gap is state, not reachability.
A focus-visible outline already exists. The trap does not wrap forward Tab, and modal close does not return focus.
PhotoSwipe is a dialog. Cart, menu, search, quick shop, and popups are not.
Submenus open, but expanded state, duplicate ids, and the mobile logo name fail.
Insta video reel does not autoplay. The decorative second reel track is correctly hidden.
Announcement bar, buttons, and product badges pass. Footer and chips do not.
Decorative icons inside named controls should stay hidden. Do not add roles to native controls.
The cart count is not announced. Filter and sort changes do not send a status. Confirm variant updates before adding live regions.
See the notes in this card. These items are documented rather than given a single issue id.
Product form, filters, search, and the cart drawer are theme-owned. Shop Pay and content_for_header stay with Shopify.
See the notes in this card. These items are documented rather than given a single issue id.
Klaviyo, Syndeca, Kiwi, Syte, Klarna, TrueVault, and chat widgets are app-owned. Do not patch them from the theme.
See the notes in this card. These items are documented rather than given a single issue id.
Color changes required for WCAG 2.2 AA. Samples show the current pair. Announcement text and buttons pass and should stay.
| ID | Surface | Sample | Ratio | AA |
|---|---|---|---|---|
A11Y-023 |
Footer text | #a3a2a2 on #ffffff | 2.55:1 | Fail |
A11Y-024 |
Footer field | #ffffff on #ffffff | 1:1 | Fail |
A11Y-025 |
Filter chips | #b8b8b8 on #ffffff | 1.98:1 | Fail |
A11Y-035 |
Sort prefix | #8a8680 on #ffffff | 3.62:1 | Fail |
A11Y-036 |
New badge | #beae54 on #f4efe0 | 1.79:1 | Fail |
A11Y-037 |
Placeholders | #808080 on #ffffff | 3.95:1 | Fail |
A11Y-044 |
Helper text | #cccccc on #ffffff | 1.61:1 | Fail |
Pass |
Announcement | #1c1d1d on #e7a3e9 | 8.7:1 | Pass |
Pass |
Buttons | #ffffff on #111111 | 17:1 | Pass |
Target for normal text is 4.5:1. A safer footer text color on white is #595959. #767676 is the minimum that clears AA.
Do not patch these from the theme. The connection-verification wall seen in WAVE is not theme markup.
| Source | Where | Ownership |
|---|---|---|
| negate.io | layout/theme.liquid line 4 | Tracking script. No theme UI. |
| TrueVault Polaris | theme.liquid lines 8–9; footer-menu | Privacy UI is the app. The theme image alt is already set. |
| Google Tag Manager, Ads, GA4 | theme.liquid head | Tags. Widgets they inject are outside the theme. |
| Kiwi sizing | kiwiSizing.liquid via theme.liquid | Size widget UI is injected. |
| ETS contact | three page ids | App date picker. Not a theme form. |
| Klaviyo | back-in-stock and add-to-cart snippets | Back-in-stock modal is app UI. |
| Syndeca | theme.liquid lines 246–255 | Shoppable image. The theme only handles the click. |
| Klarna | KlarnaThemeGlobals | Messaging widget is the app. |
| Syte | camera button and product cards | Visual search UI is the app. The theme camera button is named. |
| Smile, Swym, MNTN | snippets present | Smile and MNTN are not rendered from theme.liquid. |
| redo chat | class hook in theme.js | Chat widget is the app. |
theme.css.liquid mirrors theme.css. Confirm which file Shopify serves before editing either. Do not edit jQuery, Flickity, vendor scripts, or app snippets.
assets/theme.js — focus trap, modal focus restore, price-slider namesassets/custom.js — sort keyboard behavior, only if the native select is not restoredassets/drawer-logic.js — cart upsell togglesnippets/collection-sort.liquidsnippets/collection-grid-filters-form.liquidsections/hero-video.liquidsections/main-banner.liquidsections/header.liquidsections/royalty-header.liquidsections/ymig-header.liquidsnippets/header-logo-block.liquidsnippets/header-desktop-nav.liquidsnippets/header-icons.liquidsnippets/drawer-menu.liquidsnippets/royalty-drawer-menu.liquidsnippets/ymig-drawer-menu.liquidsnippets/cart-drawer.liquidsnippets/royalty-cart-drawer.liquidsnippets/quick-shop-modal.liquidsnippets/video-modal.liquidsnippets/product-form.liquidsnippets/product-grid-item.liquidsnippets/product-grid-item-variant.liquidsnippets/product-images.liquidsnippets/media.liquidsnippets/variant-button.liquidsnippets/newsletter-form.liquidsnippets/footer-newsletter.liquidsnippets/newsletter-reminder.liquidsnippets/search-bar.liquidsnippets/tool-tip-trigger.liquidsnippets/promo-sliders.liquidsnippets/collection-my-size.liquidsnippets/pagination.liquidsections/newsletter-popup.liquidsections/rescue-popup.liquidsections/slideshow.liquidsections/faq.liquidsections/main-collection.liquidsections/main-collection-variant.liquidsections/main-cart.liquidsections/main-page.liquidsections/logo-list.liquidsections/as-seen-in.liquidsections/insta-reel.liquidsections/contact-form.liquidsections/article-template.liquidsnippets/announcement-bar.liquidsnippets/tab-contact.liquidsnippets/quantity-input.liquidsnippets/multi-selectors.liquidblocks/ai_gen_block_9fd727f.liquidtemplates/customers/login.liquidtemplates/customers/register.liquidtemplates/customers/addresses.liquidtemplates/customers/reset_password.liquidtemplates/customers/activate_account.liquidlayout/theme.liquid — list-collections redirect only, if merchandising agreesassets/accessibility.cssassets/custom.cssassets/theme.css — placeholders, slider ticks, and PhotoSwipe error text only| Fix | Layout | CSS | JavaScript | Shopify | Mobile | Desktop |
|---|---|---|---|---|---|---|
| Dialog roles, labels, alt, autocomplete | None expected | None | None | None | None | None |
| Focus trap and focus restore | None | None | Cart, menu, search, quick shop, tooltips | Must still open and close | Drawer scroll lock must stay | Same |
| Sort control | Keep the current look | Possible | Sort reload must keep working | Query params must stay | Duplicated in the filter form | Same |
| Filter ids and div-to-button | Padding already exists | None if classes stay | Filter submit and chips | Collection filtering | Mobile drawer | Sidebar |
| Collection h1 | Prefer a visually hidden h1 so mobile layout stays | Clip utility only | None | None | Title stays visually hidden | Unchanged |
| Brand h1 to span | None if the class stays | None | header-switch.js must still find .logo | Brand switching | Same | Same |
| Footer and chip colors | None | Color only | None | None | Same | Same |
| Hero pause button | Use the existing media button slot | Minimal | Must pause the video element | None | Same | Same |
| Promo slider pause | One extra control | Minimal | Flickity player API | None | Same | Same |
| Three headers | High if a header is removed. Prefer unique ids. | None | Depends on #header, #royalty-header, #ymig-header | Brand session | Same | Same |
| List-collections redirect | None | None | Removing it changes /collections routing | Merchandising | Same | Same |
Phase 2 should start with A11Y-001 through A11Y-004, then the rest of the serious group, then moderate, then minor.