From fb1afb72d48e6258f0cb598183b2b950896e65d3 Mon Sep 17 00:00:00 2001 From: sdarbinyan Date: Fri, 24 Jul 2026 08:46:18 +0400 Subject: [PATCH] fix(storefront): WCAG 2.1 AA accessibility fixes RC A11Y-01 audit pass, storefront + shared app-shell chrome only. Builds on RC-Visual-02/RC-Premium-01/RC STORE-01 without redoing that work. - Skip link: added first-focusable "skip to main content" link (app.html, styles.scss .skip-link/.sr-only), targeting new #main-content landmark. New app.skipToContent i18n key in en/ru/hy. - Header: mobile menu items stayed keyboard-focusable and screen-reader reachable while visually collapsed (max-height:0 with no visibility toggle) - fixed with visibility:hidden + matched transition-delay. Desktop search input (readonly, click-to-navigate) had no keyboard activation - added aria-label + (keydown.enter). - Cart payment/bank-payment modals: custom (non-app-dialog) UI had no focus trap, no Escape handling, and never returned focus to the triggering element - ported app-dialog's confirmed-correct focus-trap/Escape/return-focus pattern directly onto cart.component.ts. Added role="dialog"/aria-modal/aria-label to both panels and role="status"|"alert"/aria-live to every payment-status screen so screen readers announce state changes (creating/waiting/success/ error/timeout). - Search combobox: suggestion listbox had no role="combobox" wiring on the input and suggestion buttons weren't role="option" - added aria-autocomplete, aria-controls, aria-activedescendant, aria-selected so the existing arrow-key navigation is announced to screen readers. - Product tabs: tablist/tab pattern was incomplete (no role="tablist", no tabpanel) - added role="tablist" + ids to product-tabs.component, role="tabpanel"/aria-labelledby to the content panel in product-details-container. - Review form: rating/text validation errors weren't associated with their controls (no aria-describedby, no role="alert") - fixed; added aria-required to the review textarea. - delivery-selector: added aria-required to the delivery + + } diff --git a/src/app/components/header/header.component.scss b/src/app/components/header/header.component.scss index 090fce3..e7da139 100644 --- a/src/app/components/header/header.component.scss +++ b/src/app/components/header/header.component.scss @@ -509,19 +509,26 @@ z-index: 999; box-sizing: border-box; - // Hide by default, animate in + // Hide by default, animate in. `visibility: hidden` (with a matched + // transition-delay so it only applies once the collapse finishes) keeps + // the menu's links/buttons out of the Tab order and the accessibility + // tree while closed — without it they stayed keyboard-focusable and + // screen-reader-reachable despite being visually collapsed to 0 height. max-height: 0; padding-top: 0; padding-bottom: 0; overflow: hidden; opacity: 0; - transition: max-height 0.35s ease, opacity 0.25s ease, padding 0.35s ease; + visibility: hidden; + transition: max-height 0.35s ease, opacity 0.25s ease, padding 0.35s ease, visibility 0s linear 0.35s; &.platform-mobile-menu-open { max-height: calc(100dvh - 84px); padding: 28px 20px 32px; opacity: 1; + visibility: visible; overflow-y: auto; + transition: max-height 0.35s ease, opacity 0.25s ease, padding 0.35s ease, visibility 0s linear 0s; } } diff --git a/src/app/features/search/components/search-bar/search-bar.component.html b/src/app/features/search/components/search-bar/search-bar.component.html index c74f766..30a3013 100644 --- a/src/app/features/search/components/search-bar/search-bar.component.html +++ b/src/app/features/search/components/search-bar/search-bar.component.html @@ -14,7 +14,11 @@ (blur)="onBlur()" name="query" [placeholder]="'catalog.searchPlaceholder' | translate" + role="combobox" + aria-autocomplete="list" [attr.aria-expanded]="suggestions.length > 0" + [attr.aria-controls]="suggestions.length > 0 ? 'search-suggestions-listbox' : null" + [attr.aria-activedescendant]="activeSuggestionIndex() >= 0 ? 'search-suggestion-' + activeSuggestionIndex() : null" [attr.aria-label]="'catalog.searchPlaceholder' | translate" autocomplete="off" /> @@ -27,14 +31,17 @@ @if (suggestions.length > 0) { -
+
{{ 'catalog.suggestionsTitle' | translate }}
@for (item of suggestions; track item.id) { @if (paymentStatus() === 'creating') { -
+

{{ 'cart.creatingPayment' | translate }}

{{ 'cart.waitFewSeconds' | translate }}

@@ -208,7 +208,7 @@ } @if (paymentStatus() === 'waiting') { -
+

{{ selectedPaymentMethod() === 'card' ? ('cart.waitingPayment' | translate) : ('cart.scanQr' | translate) }}

@if (qrCodeUrl()) { @@ -246,7 +246,7 @@ } @if (paymentStatus() === 'error') { -
+