{"id":29925,"date":"2025-04-23T09:48:25","date_gmt":"2025-04-23T07:48:25","guid":{"rendered":"https:\/\/www.angulararchitects.io\/?p=29925"},"modified":"2025-04-23T10:07:42","modified_gmt":"2025-04-23T08:07:42","slug":"accessible-angular-forms","status":"publish","type":"post","link":"https:\/\/www.angulararchitects.io\/en\/blog\/accessible-angular-forms\/","title":{"rendered":"Building Accessible Forms with Angular"},"content":{"rendered":"<div class=\"wp-post-series-box series-accessibility-in-angular-en wp-post-series-box--expandable\">\n\t\t\t<input id=\"collapsible-series-accessibility-in-angular-en6a34f5f3c6a78\" class=\"wp-post-series-box__toggle_checkbox\" type=\"checkbox\">\n\t\n\t<label\n\t\tclass=\"wp-post-series-box__label\"\n\t\t\t\t\tfor=\"collapsible-series-accessibility-in-angular-en6a34f5f3c6a78\"\n\t\t\ttabindex=\"0\"\n\t\t\t\t>\n\t\t<p class=\"wp-post-series-box__name wp-post-series-name\">\n\t\t\tThis is post 5 of 6 in the series <em>&ldquo;Accessibility in Angular&rdquo;<\/em>\t\t<\/p>\n\t\t\t<\/label>\n\n\t\t\t<div class=\"wp-post-series-box__posts\">\n\t\t\t<ol>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/web-accessibility-in-angular\/\">Web Accessibility (A11y) in Angular \u2013 Introduction<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/accessibility-testing-tools\/\">Accessibility Testing Tools for Angular<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/accessible-angular-routes\/\">Accessible Angular Routes<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/aria-roles-attributes\/\">ARIA roles and attributes in Angular<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><span class=\"wp-post-series-box__current\">Building Accessible Forms with Angular<\/span><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/angular-aria\/\">Why Angular ARIA in v21 is pretty neat<\/a><\/li>\n\t\t\t\t\t\t\t<\/ol>\n\t\t<\/div>\n\t<\/div>\n<p>Accessible <em>Angular Forms<\/em> are essential to ensure that all our users \u2013 including those with disabilities \u2013 can interact with our <em>Angular App<\/em> effectively. By implementing forms with Accessibility (A11y) in mind, we meet legal (<a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/web-accessibility-in-angular\/\">EAA 2025<\/a> \u267f) and ethical standards, and create a more inclusive experience. Accessible forms work better with screen readers, keyboard navigation, and assistive technologies \u2013 which not only helps users with impairments but also <strong>enhances the overall UX<\/strong> of our <em>Angular Apps<\/em>.<\/p>\n<h2>Angular Forms<\/h2>\n<p>In <em>Angular,<\/em> we have two types of forms:<\/p>\n<ul>\n<li><strong>Template-driven Forms<\/strong>: These are simpler and more declarative, relying on <em>Angular Directives<\/em> to create forms. They are built around the <code>NgModel<\/code> directive and generally used for simple forms. As the name suggests, these are implemented in the template through attributes.<\/li>\n<li><strong>Reactive Forms<\/strong>: These are more powerful and flexible, allowing for complex form structures and dynamic validation. They are built in the component class around the <code>FormGroup<\/code> and <code>FormControl<\/code> classes. Most often the <code>FormBuilder<\/code> service is used to create the forms.<\/li>\n<\/ul>\n<p>In 2025, the <em>Angular team<\/em> is expected to work on <strong><a href=\"https:\/\/angular.dev\/roadmap#improving-the-angular-developer-experience\">Signal integration<\/a><\/strong> for <em>Angular Forms<\/em>.<\/p>\n<p>However, no matter which type of form you choose \u2013 and whether you migrate from Observables to Signals or not \u2013 the goal is to ensure that your forms are accessible to all users. This includes providing proper labels, error messages, and keyboard navigation. To keep the focus on A11y, we will use the simpler template-driven approach without any reactivity in our examples.<\/p>\n<h2>Keyboard Navigation &amp; Tab Focus<\/h2>\n<p>Keyboard navigation is a critical part of form A11y. Many users rely on the keyboard \u2013 rather than a mouse \u2013 to move through a form using the Tab, Shift + Tab, Arrow, and Enter keys. Ensuring a logical tab order, using semantic HTML elements like <code>&lt;label&gt;<\/code>, <code>&lt;input&gt;<\/code>, <code>&lt;button&gt;<\/code>, and avoiding custom controls that break default behavior, helps users navigate efficiently. You should not change the order. However, you can add <code>tabindex=&quot;0&quot;<\/code> to include non-interactive elements or custom components, and use <code>tabindex=&quot;-1&quot;<\/code> to remove elements from the tab order.<\/p>\n<p>Additionally, visual focus indicators (like outlines, at least 2 if not 3px width) should be clearly visible to show which element is currently active. When done right, keyboard-friendly forms not only improve A11y but also lead to a smoother and more intuitive experience for all users \ud83d\ude0e<\/p>\n<h2>Form Fields<\/h2>\n<h3>Labels &amp; type attribute<\/h3>\n<p>To ensure A11y, always associate <code>&lt;label&gt;<\/code> elements with their corresponding form controls using the for and id attributes. This improves support for screen readers and enables better keyboard navigation \u2013 clicking a label should focus the related input. Make sure each id is unique, especially when using multiple forms on the same page. Additionally, specify the correct type for all <code>&lt;input&gt;<\/code> and <code>&lt;button&gt;<\/code> elements to ensure proper behavior, like submitting a form when pressing Enter on a <code>&lt;button type=&quot;submit&quot;&gt;<\/code>.<\/p>\n<pre><code class=\"language-html\">&lt;label for=&quot;from&quot;&gt;From&lt;\/label&gt; &lt;input type=&quot;text&quot; name=&quot;from&quot; id=&quot;from&quot; [...] \/&gt;<\/code><\/pre>\n<h3>Grouping fields<\/h3>\n<p>When we have a group of related inputs, especially radio buttons or checkboxes, screen readers benefit from extra semantic context by <code>&lt;fieldset&gt;<\/code> and <code>&lt;legend&gt;<\/code>:<\/p>\n<pre><code class=\"language-html\">&lt;fieldset&gt;\n  &lt;legend&gt;Flight Class&lt;\/legend&gt;\n  &lt;label for=&quot;economy&quot;&gt;Economy&lt;\/label&gt;\n  &lt;input type=&quot;radio&quot; name=&quot;class&quot; id=&quot;economy&quot; value=&quot;economy&quot; \/&gt;\n  &lt;label for=&quot;business&quot;&gt;Business&lt;\/label&gt;\n  &lt;input type=&quot;radio&quot; name=&quot;class&quot; id=&quot;business&quot; value=&quot;business&quot; \/&gt;\n&lt;\/fieldset&gt;<\/code><\/pre>\n<p>This provides context for assistive tech. Without this, users may hear &quot;Economy&quot; and &quot;Business&quot; without understanding they are part of a group. The <code>&lt;fieldset&gt;<\/code> element groups related controls, while the <code>&lt;legend&gt;<\/code> provides a caption for the group.<\/p>\n<h3>Required fields<\/h3>\n<p>When a form element (like <code>&lt;input&gt;<\/code>, <code>&lt;select&gt;<\/code>, or <code>&lt;textarea&gt;<\/code>) must have a value, use the <code>required<\/code> attribute. This prevents form submission unless the required fields are filled out, and helps users with assistive technologies understand which fields need valid content. For example, add the <code>required<\/code> attribute to both input fields in your form, and consider adding an asterisk (*) as an additional visual indicator.<\/p>\n<pre><code class=\"language-html\">&lt;label for=&quot;name&quot;&gt;Name (*)&lt;\/label&gt; &lt;input type=&quot;text&quot; name=&quot;name&quot; id=&quot;name&quot; required \/&gt;<\/code><\/pre>\n<h3>Autocomplete<\/h3>\n<p>The <code>autocomplete<\/code> attribute is a powerful tool for improving the user experience in forms. It allows browsers to remember and suggest previously entered values, making it easier for users to fill out forms quickly. By using the <code>autocomplete<\/code> attribute, you can specify the type of data expected in each field, such as <code>name<\/code>, <code>email<\/code>, or <code>address<\/code>. This not only enhances usability but also helps with A11y by providing clear context for screen readers and assistive technologies.<\/p>\n<pre><code class=\"language-html\">&lt;input type=&quot;text&quot; name=&quot;phone&quot; autocomplete=&quot;phone&quot; \/&gt;<\/code><\/pre>\n<p>To avoid autocomplete, we should set the <code>autocomplete<\/code> attribute to <code>off<\/code>. This is especially useful for sensitive information like passwords or when you want to ensure that users enter fresh data.<\/p>\n<pre><code class=\"language-html\">&lt;input type=&quot;password&quot; name=&quot;password&quot; autocomplete=&quot;off&quot; \/&gt;<\/code><\/pre>\n<h3>ARIA attributes<\/h3>\n<p>ARIA attributes (more on them in our <a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/aria-roles-attributes\/\">last post<\/a>) can enhance the A11y of our <em>Angular Forms<\/em> by providing additional context to assistive technologies when native HTML alone isn\u2019t enough. Attributes like <code>aria-label<\/code> or <code>aria-labelledby<\/code> can offer accessible names for form controls when visible labels aren\u2019t practical. While ARIA should never replace semantic HTML, it\u2019s a powerful tool to bridge A11y gaps and ensure all users can understand and interact with your forms effectively.<\/p>\n<pre><code class=\"language-html\">&lt;input type=&quot;search&quot; name=&quot;search&quot; aria-label=&quot;Search flights&quot; placeholder=&quot;Search...&quot; \/&gt;<\/code><\/pre>\n<p>For validation, <code>aria-invalid=&quot;true&quot;<\/code> can indicate a validation error and <code>aria-describedby<\/code> can provide additional context or instructions. For example, if a user enters an invalid email address, you can set <code>aria-invalid=&quot;true&quot;<\/code> on the input field and use <code>aria-describedby<\/code> to point to an error message that explains the issue.<\/p>\n<pre><code class=\"language-html\">@let hasFromErrors = flightSearchForm.controls[&#039;from&#039;] &amp;&amp; flightSearchForm.controls[&#039;from&#039;].touched &amp;&amp; flightSearchForm.controls[&#039;from&#039;].errors;\n\n&lt;input type=&quot;text&quot; name=&quot;from&quot; id=&quot;from&quot; required [attr.aria-invalid]=&quot;!!hasFromErrors&quot; [attr.aria-describedby]=&quot;hasFromErrors ? &#039;from_error&#039; : null&quot; \/&gt;<\/code><\/pre>\n<p>Speaking about error messages, let's take a look at how to handle them.<\/p>\n<h2>Error messages<\/h2>\n<p>Accessible error messages help all users understand and correct form issues. Use <code>aria-describedby<\/code> (as in the example above) to link inputs to their error messages, and add <code>aria-live=&quot;polite&quot;<\/code> to ensure screen readers announce them when they appear. Messages should be clear, concise, and not rely on color alone \u2013 always provide text or icons for better clarity.<\/p>\n<p>Personal preferences of <strong>error messages<\/strong>:<\/p>\n<ul>\n<li>only <strong>after user interaction<\/strong> with the form: on blur (&quot;touched&quot; in <em>Angular<\/em>) or after submitting.<\/li>\n<li>focus on the <strong>first invalid control<\/strong> (see code example below) upon submitting.<\/li>\n<li><strong>inline<\/strong> with the form fields, not at the top or bottom of the form.<\/li>\n<li><strong>after<\/strong> the form field, not before.<\/li>\n<li><strong>in (dark) red<\/strong> and with an icon (e.g., \u274c) to make them more visible.<\/li>\n<\/ul>\n<pre><code class=\"language-typescript\">export class FlightSearchComponent {\n  private readonly document = inject(DOCUMENT); \/\/ for the focus\n  private readonly flightSearchForm = viewChild.required&lt;NgForm&gt;(&#039;flightSearchForm&#039;);\n\n  protected onSearch(): void {\n    if (this.flightSearchForm()?.invalid) {\n      this.markFormGroupTouched(this.flightSearchForm());\n      this.focusFirstInvalidControl(this.flightSearchForm());\n      return;\n    }\n\n    \/\/ do the search\n  }\n\n  private markFormGroupTouched(formGroup: FormGroup): void {\n    for (const key of Object.keys(formGroup.controls)) {\n      const control = formGroup.get(key);\n      if (control instanceof FormGroup) {\n        this.markFormGroupTouched(control);\n      } else {\n        control?.markAsTouched();\n      }\n    });\n  }\n\n  private focusFirstInvalidControl(formGroup: FormGroup): void {\n    for (const key of Object.keys(formGroup.controls)) {\n      const control = formGroup.get(key);\n      if (control?.invalid) {\n        const invalidControl = this.document.querySelector(<code>[name=&quot;${key}&quot;]<\/code>);\n        (invalidControl as HTMLElement)?.focus();\n        break;\n      }\n    }\n  }\n}<\/code><\/pre>\n<pre><code class=\"language-html\">&lt;form #flightSearchForm=&quot;ngForm&quot;&gt;\n  &lt;label for=&quot;fromAirport&quot;&gt;From (*)&lt;\/label&gt;\n\n  @let hasFromErrors = flightSearchForm.controls[&#039;from&#039;] &amp;&amp; flightSearchForm.controls[&#039;from&#039;].touched &amp;&amp; flightSearchForm.controls[&#039;from&#039;].errors;\n\n  &lt;input\n    type=&quot;text&quot;\n    name=&quot;from&quot;\n    id=&quot;fromAirport&quot;\n    required\n    [minlength]=&quot;minLength&quot;\n    [maxlength]=&quot;maxLength&quot;\n    [pattern]=&quot;pattern&quot;\n    [attr.aria-invalid]=&quot;!!hasFromErrors&quot;\n    [attr.aria-describedby]=&quot;hasFromErrors ? &#039;fromErrors&#039; : null&quot;\n    [(ngModel)]=&quot;from&quot;\n  \/&gt;\n\n  @if (hasFromErrors) {\n  &lt;app-flight-validation-errors id=&quot;fromErrors&quot; [errors]=&quot;flightSearchForm.controls[&#039;from&#039;].errors&quot; fieldLabel=&quot;From&quot; \/&gt;\n  }\n&lt;\/form&gt;<\/code><\/pre>\n<h2>Accessibility Workshop<\/h2>\n<p>For those looking to deepen their <em>Angular<\/em> expertise, we offer a range of workshops \u2013 both in English and German:<\/p>\n<ul>\n<li>\u267f <a href=\"https:\/\/www.angulararchitects.io\/en\/training\/angular-accessibility-workshop\/\"><strong>Accessibility Workshop<\/strong><\/a><\/li>\n<li>\ud83d\udcc8 <a href=\"https:\/\/www.angulararchitects.io\/en\/training\/angular-best-practices\/\"><strong>Best Practices Workshop<\/strong><\/a> (including accessibility-related topics)<\/li>\n<li>\ud83d\ude80 <a href=\"https:\/\/www.angulararchitects.io\/en\/training\/angular-performance-optimization-workshop\/\"><strong>Performance Workshop<\/strong><\/a><\/li>\n<\/ul>\n<h2>Conclusion<\/h2>\n<p>Building accessible forms in <em>Angular<\/em> is not just a best practice \u2013 it\u2019s a commitment to creating better experiences for everyone. With just a few thoughtful choices, you can make your forms inclusive, intuitive, and ready for the future. For more information on <em>Angular<\/em> and <em>Accessibility<\/em>, check out my <strong><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/web-accessibility-in-angular\/\">A11y blog series<\/a><\/strong>.<\/p>\n<p>This blog post was written by <a href=\"https:\/\/alex.thalhammer.name\">Alexander Thalhammer<\/a>. Follow me on <a href=\"https:\/\/at.linkedin.com\/in\/thalhammer\">Linkedin<\/a>, <a href=\"https:\/\/twitter.com\/LX_T\">X<\/a> or <a href=\"https:\/\/github.com\/L-X-T\">giThub<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is post 5 of 6 in the series &ldquo;Accessibility in Angular&rdquo; Web Accessibility (A11y) in Angular \u2013 Introduction Accessibility Testing Tools for Angular Accessible Angular Routes ARIA roles and attributes in Angular Building Accessible Forms with Angular Why Angular ARIA in v21 is pretty neat Accessible Angular Forms are essential to ensure that all [&hellip;]<\/p>\n","protected":false},"author":21,"featured_media":29977,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_price":"","_stock":"","_tribe_ticket_header":"","_tribe_default_ticket_provider":"","_ticket_start_date":"","_ticket_end_date":"","_tribe_ticket_show_description":"","_tribe_ticket_show_not_going":false,"_tribe_ticket_use_global_stock":"","_tribe_ticket_global_stock_level":"","_global_stock_mode":"","_global_stock_cap":"","_tribe_rsvp_for_event":"","_tribe_ticket_going_count":"","_tribe_ticket_not_going_count":"","_tribe_tickets_list":"[]","_tribe_ticket_has_attendee_info_fields":false,"footnotes":""},"categories":[18],"tags":[],"class_list":["post-29925","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","post_series-accessibility-in-angular-en"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.1.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Building Accessible Forms with Angular - ANGULARarchitects<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.angulararchitects.io\/en\/blog\/accessible-angular-forms\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Building Accessible Forms with Angular - ANGULARarchitects\" \/>\n<meta property=\"og:description\" content=\"This is post 5 of 6 in the series &ldquo;Accessibility in Angular&rdquo; Web Accessibility (A11y) in Angular \u2013 Introduction Accessibility Testing Tools for Angular Accessible Angular Routes ARIA roles and attributes in Angular Building Accessible Forms with Angular Why Angular ARIA in v21 is pretty neat Accessible Angular Forms are essential to ensure that all [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.angulararchitects.io\/en\/blog\/accessible-angular-forms\/\" \/>\n<meta property=\"og:site_name\" content=\"ANGULARarchitects\" \/>\n<meta property=\"article:published_time\" content=\"2025-04-23T07:48:25+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-04-23T08:07:42+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2025\/04\/a11y-forms-hero-scaled.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"2560\" \/>\n\t<meta property=\"og:image:height\" content=\"1882\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Alexander Thalhammer\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@LX_T\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Alexander Thalhammer\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"8 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/accessible-angular-forms\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/accessible-angular-forms\/\"},\"author\":{\"name\":\"Alexander Thalhammer\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/eefb0cd4d115dfd406a02b6dbc760d45\"},\"headline\":\"Building Accessible Forms with Angular\",\"datePublished\":\"2025-04-23T07:48:25+00:00\",\"dateModified\":\"2025-04-23T08:07:42+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/accessible-angular-forms\/\"},\"wordCount\":1038,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/accessible-angular-forms\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2025\/04\/a11y-forms-hero-1-scaled.jpg\",\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.angulararchitects.io\/en\/blog\/accessible-angular-forms\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/accessible-angular-forms\/\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/blog\/accessible-angular-forms\/\",\"name\":\"Building Accessible Forms with Angular - ANGULARarchitects\",\"isPartOf\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/accessible-angular-forms\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/accessible-angular-forms\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2025\/04\/a11y-forms-hero-1-scaled.jpg\",\"datePublished\":\"2025-04-23T07:48:25+00:00\",\"dateModified\":\"2025-04-23T08:07:42+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/accessible-angular-forms\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.angulararchitects.io\/en\/blog\/accessible-angular-forms\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/accessible-angular-forms\/#primaryimage\",\"url\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2025\/04\/a11y-forms-hero-1-scaled.jpg\",\"contentUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2025\/04\/a11y-forms-hero-1-scaled.jpg\",\"width\":2560,\"height\":1506,\"caption\":\"Accessibility Forms Hero\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/accessible-angular-forms\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.angulararchitects.io\/en\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Building Accessible Forms with Angular\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#website\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/\",\"name\":\"ANGULARarchitects\",\"description\":\"AngularArchitects.io\",\"publisher\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.angulararchitects.io\/en\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\",\"name\":\"ANGULARarchitects\",\"alternateName\":\"SOFTWAREarchitects\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg\",\"contentUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg\",\"width\":644,\"height\":216,\"caption\":\"ANGULARarchitects\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/github.com\/angular-architects\",\"https:\/\/www.linkedin.com\/company\/angular-architects\/\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/eefb0cd4d115dfd406a02b6dbc760d45\",\"name\":\"Alexander Thalhammer\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/23f1b6f9b1ee7d04247b8320851762347d56c76b1537d100d07390d6d919b78d?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/23f1b6f9b1ee7d04247b8320851762347d56c76b1537d100d07390d6d919b78d?s=96&d=mm&r=g\",\"caption\":\"Alexander Thalhammer\"},\"sameAs\":[\"https:\/\/x.com\/LX_T\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Building Accessible Forms with Angular - ANGULARarchitects","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.angulararchitects.io\/en\/blog\/accessible-angular-forms\/","og_locale":"en_US","og_type":"article","og_title":"Building Accessible Forms with Angular - ANGULARarchitects","og_description":"This is post 5 of 6 in the series &ldquo;Accessibility in Angular&rdquo; Web Accessibility (A11y) in Angular \u2013 Introduction Accessibility Testing Tools for Angular Accessible Angular Routes ARIA roles and attributes in Angular Building Accessible Forms with Angular Why Angular ARIA in v21 is pretty neat Accessible Angular Forms are essential to ensure that all [&hellip;]","og_url":"https:\/\/www.angulararchitects.io\/en\/blog\/accessible-angular-forms\/","og_site_name":"ANGULARarchitects","article_published_time":"2025-04-23T07:48:25+00:00","article_modified_time":"2025-04-23T08:07:42+00:00","og_image":[{"width":2560,"height":1882,"url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2025\/04\/a11y-forms-hero-scaled.jpg","type":"image\/jpeg"}],"author":"Alexander Thalhammer","twitter_card":"summary_large_image","twitter_creator":"@LX_T","twitter_misc":{"Written by":"Alexander Thalhammer","Est. reading time":"8 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/accessible-angular-forms\/#article","isPartOf":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/accessible-angular-forms\/"},"author":{"name":"Alexander Thalhammer","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/eefb0cd4d115dfd406a02b6dbc760d45"},"headline":"Building Accessible Forms with Angular","datePublished":"2025-04-23T07:48:25+00:00","dateModified":"2025-04-23T08:07:42+00:00","mainEntityOfPage":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/accessible-angular-forms\/"},"wordCount":1038,"commentCount":0,"publisher":{"@id":"https:\/\/www.angulararchitects.io\/en\/#organization"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/accessible-angular-forms\/#primaryimage"},"thumbnailUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2025\/04\/a11y-forms-hero-1-scaled.jpg","inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.angulararchitects.io\/en\/blog\/accessible-angular-forms\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/accessible-angular-forms\/","url":"https:\/\/www.angulararchitects.io\/en\/blog\/accessible-angular-forms\/","name":"Building Accessible Forms with Angular - ANGULARarchitects","isPartOf":{"@id":"https:\/\/www.angulararchitects.io\/en\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/accessible-angular-forms\/#primaryimage"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/accessible-angular-forms\/#primaryimage"},"thumbnailUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2025\/04\/a11y-forms-hero-1-scaled.jpg","datePublished":"2025-04-23T07:48:25+00:00","dateModified":"2025-04-23T08:07:42+00:00","breadcrumb":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/accessible-angular-forms\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.angulararchitects.io\/en\/blog\/accessible-angular-forms\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/accessible-angular-forms\/#primaryimage","url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2025\/04\/a11y-forms-hero-1-scaled.jpg","contentUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2025\/04\/a11y-forms-hero-1-scaled.jpg","width":2560,"height":1506,"caption":"Accessibility Forms Hero"},{"@type":"BreadcrumbList","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/accessible-angular-forms\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.angulararchitects.io\/en\/"},{"@type":"ListItem","position":2,"name":"Building Accessible Forms with Angular"}]},{"@type":"WebSite","@id":"https:\/\/www.angulararchitects.io\/en\/#website","url":"https:\/\/www.angulararchitects.io\/en\/","name":"ANGULARarchitects","description":"AngularArchitects.io","publisher":{"@id":"https:\/\/www.angulararchitects.io\/en\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.angulararchitects.io\/en\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.angulararchitects.io\/en\/#organization","name":"ANGULARarchitects","alternateName":"SOFTWAREarchitects","url":"https:\/\/www.angulararchitects.io\/en\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/","url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg","contentUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg","width":644,"height":216,"caption":"ANGULARarchitects"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/github.com\/angular-architects","https:\/\/www.linkedin.com\/company\/angular-architects\/"]},{"@type":"Person","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/eefb0cd4d115dfd406a02b6dbc760d45","name":"Alexander Thalhammer","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/23f1b6f9b1ee7d04247b8320851762347d56c76b1537d100d07390d6d919b78d?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/23f1b6f9b1ee7d04247b8320851762347d56c76b1537d100d07390d6d919b78d?s=96&d=mm&r=g","caption":"Alexander Thalhammer"},"sameAs":["https:\/\/x.com\/LX_T"]}]}},"_links":{"self":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/29925","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/users\/21"}],"replies":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/comments?post=29925"}],"version-history":[{"count":1,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/29925\/revisions"}],"predecessor-version":[{"id":29926,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/29925\/revisions\/29926"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/media\/29977"}],"wp:attachment":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/media?parent=29925"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/categories?post=29925"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/tags?post=29925"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}