The European Accessibility Act in Practice
bankingNovember 5, 2026

The European Accessibility Act in Practice

Auditing an Angular banking portal

Since 28 June 2025, the European Accessibility Act (EAA) has applied to consumer banking services across the EU. Internet banking portals and mobile apps now have to be accessible to customers with disabilities, and national authorities can enforce it. Many bank portals are built with Angular Material, and teams often assume that accessible components make an accessible portal. Audits show otherwise. The components are a good foundation, but most failures appear where they are combined into real banking screens: multi-step payment forms, transaction tables, session timeouts and authentication steps. 

Let's explain what the EAA expects from a banking portal, how we run a structured audit of an Angular Material application, and the failures we find most often in forms and data tables, with the fixes. 


What the EAA expects from a banking portal 

The EAA is Directive (EU) 2019/882, transposed into national law in each member state (in Romania, by Law 232/2022). For banks, the key points are: 

Consumer banking services are in scope. That includes payment services, payment accounts, credit agreements and e-money, delivered through websites and mobile apps. 

The requirements follow four principles. Services must be perceivable, operable, understandable and robust, the same principles as the Web Content Accessibility Guidelines (WCAG). 

The harmonised standard is EN 301 549, which incorporates WCAG 2.1 at level AA for web content. Meeting it gives a presumption of conformity. WCAG 2.2 adds criteria that are especially relevant to banking, such as accessible authentication, and is worth targeting too. 

Banking information must be understandable. For consumer banking services, the Act requires that information is understandable at a level not exceeding B2 (upper intermediate) on the Council of Europe's language scale. That applies to the content, not just the code. 

Microenterprises are exempt for services, which in practice excludes very few banks. 


The practical question for a development team is simple: does the portal meet WCAG 2.1 AA in the journeys customers actually use? 


A structured audit in six steps 


A useful audit is not a one-off scan. It is a repeatable process with clear scope and findings that developers can act on. 

Scope the critical journeys. Login and authentication, account overview, transaction history, domestic and SEPA payments, beneficiary management, statements and documents, and one application flow such as a loan or card request. Audit the journeys end to end, not pages in isolation. 

Run automated checks. Tools such as axe catch missing labels, contrast failures and invalid ARIA quickly. They find only part of the problems, so treat them as a first filter. 

Keyboard-only pass. Complete every journey without a mouse. Check the focus order, a visible focus indicator, no keyboard traps and that every action is reachable. 

Screen reader pass. Use NVDA with Chrome or Firefox on Windows, and VoiceOver on macOS and iOS. Listen to what is announced at each step: labels, errors, status changes and table headers. 

Zoom and reflow. Check the portal at 200% zoom and at a 320 CSS pixel width, where WCAG requires content to reflow without horizontal scrolling. Wide transaction tables usually fail here. 

Report by WCAG criterion and severity. Each finding names the WCAG success criterion, the component and journey affected, the impact on customers and a proposed fix. Severity reflects whether a customer can still complete the task. 


What Angular Material gives you, and what it doesn't 

Angular Material components handle a lot of accessibility work: form fields connect labels, hints and errors to their inputs, dialogs trap and restore focus, menus and selects support the keyboard, and mat-table renders real table semantics when used on a <table> element. 

What Material can't do is make decisions that depend on the content and the journey. It doesn't know that a red amount means a debit, that an error should move focus, that a session is about to expire or that a custom theme has failed contrast. Those are the areas where audits find most problems. 


Forms: the most common failures 


Payment forms are where customers with disabilities most often get stuck. The failures we find repeatedly: 

Placeholders used as labels. The label disappears as soon as the customer starts typing, and some screen readers don't announce placeholders reliably. 

Errors that aren't announced. The field turns red, but a screen reader user hears nothing after pressing "Continue". 

Focus left on the submit button after validation fails, so the customer has to search for the problem. 

Input masks on IBAN and amount fields that rewrite the value as the customer types and confuse screen readers. 

Required fields marked only with an asterisk or colour. 


 1 Material form fields solve the first problem when used as designed. A mat-label, mat-hint and mat-error inside a mat-form-field are connected to the input automatically through aria-describedby: 
 2  
 3   Recipient IBAN 
 4    
 5   Starts with the country code, for example RO49 
 6   @if (form.controls.iban.hasError('iban')) { 
 7     Enter a valid IBAN. Check the country code and the number of characters. 
 8   } 
 9  

Don't add your own aria-describedby to the input, because it can replace the connections Material manages. And make the error message say how to fix the problem, not just that something is wrong. 

The second and third problems need a few lines in the component. On a failed submit, mark all fields as touched, announce the number of errors and move focus to the first invalid field: 

 1 private readonly announcer = inject(LiveAnnouncer); 
 2 private readonly host = inject>(ElementRef); 
 3  
 4 submit(): void { 
 5   if (this.form.invalid) { 
 6     this.form.markAllAsTouched(); 
 7     const count = countInvalidControls(this.form); 
 8     this.announcer.announce( 
 9       `${count} ${count === 1 ? 'field needs' : 'fields need'} attention.`, 'assertive'); 
10     this.host.nativeElement 
11       .querySelector('[formControlName].ng-invalid') 
12       ?.focus(); 
13     return; 
14   } 
15   this.payments.submit(this.form.getRawValue()); 
16 } 

For IBAN and amount fields, format the value when the field loses focus, not on every keystroke, and accept common variations such as spaces in an IBAN. 


Data tables: transaction lists that work for everyone 


Transaction history is the most-used screen in many portals, and the most common source of table failures: 

Tables built from div elements, so screen readers can't announce column headers or move by row and column. 

Debits and credits shown only by colour, red and green, which fails WCAG's use-of-colour criterion and is unreadable for many colour-blind customers. 

Sort changes that aren't announced, so a screen reader user doesn't know the order changed. 

Responsive layouts that turn rows into cards and lose the link between each value and its header. 

A mat-table on a native <table> element keeps real table semantics. The amount column carries the debit or credit as text, not only as colour: 

 1  2        (matSortChange)="announceSort($event)"> 
 3 4 5 
 6      8 91011 
12     131718192021
Transactions for current account ending 1234
7 sortActionDescription="Sort by booking date">Date {{ t.bookingDate | date: 'd MMM y' }} Amount 14 {{ t.direction === 'DEBIT' ? 'Debit' : 'Credit' }} 15 {{ t.amount | currency: t.currency }} 16
22 announceSort(sort: Sort): void { 23 this.announcer.announce( 24 sort.direction ? `Sorted by ${sort.active}, ${sort.direction}ending` : 'Sorting removed'); 25 }

The visible minus or plus sign is hidden from screen readers, because they read it inconsistently, and replaced with the word "Debit" or "Credit". For small screens, keep the table and let it scroll horizontally inside a labelled region, or provide a list view with each value explicitly labelled. Don't simply restyle table rows as cards with the headers removed. 


Three banking-specific checks audits often miss 


Session timeouts. Internet banking sessions expire for good security reasons, but WCAG requires that users are warned and can extend the time. A MatDialog opened before expiry, configured with role: 'alertdialog', with a clear "Stay signed in" button, meets this. The warning should come early enough for someone using a screen reader or switch access to respond. 

Authentication steps. One-time codes, device approvals and security questions are where many customers with cognitive or motor impairments get stuck. Allow pasting codes, never block password managers, and give enough time to switch apps. WCAG 2.2's "Accessible Authentication" criterion describes these expectations well. 

Documents and plain language. PDF statements and contracts are part of the service and need to be accessible too, with tags, reading order and real text. The EAA's B2 language requirement also applies to fee explanations, error messages and product information, which makes it as much a content task as a development task. 


Keeping it fixed after the audit 


Audit findings fade quickly if nothing changes in the delivery process. The practices that keep a portal accessible: 

Automated checks in CI on the critical journeys, failing the build on new violations. 

Accessible patterns in the shared component library, such as the error-focus behaviour and the amount cell above, so teams reuse fixes instead of re-solving them. 

Accessibility in the definition of done, including a keyboard pass for every new screen. 

A screen reader check before each major release, by someone trained to do it. 

An up-to-date accessibility statement, explaining the portal's conformance and how customers can report problems. 


Closing thought 

The European Accessibility Act turns accessibility from a good intention into an obligation for every bank that serves consumers in the EU. For teams using Angular Material, the foundation is already strong. What decides compliance is everything built on top: how forms report errors, how tables convey meaning without colour, how sessions and authentication treat people who need more time, and how clearly the bank explains itself. A structured audit finds those gaps; shared patterns and a few habits in the delivery process keep them closed.