<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Alexander Thalhammer</title>
    <description>The latest articles on DEV Community by Alexander Thalhammer (@lxt).</description>
    <link>https://dev.to/lxt</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1730783%2F7e7043e6-efa4-4619-a957-68057f3eb313.jpg</url>
      <title>DEV Community: Alexander Thalhammer</title>
      <link>https://dev.to/lxt</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lxt"/>
    <language>en</language>
    <item>
      <title>Agentic Engineering: How to Set Up Your Harness for Angular Development</title>
      <dc:creator>Alexander Thalhammer</dc:creator>
      <pubDate>Fri, 26 Jun 2026 08:12:15 +0000</pubDate>
      <link>https://dev.to/lxt/agentic-engineering-how-to-set-up-your-harness-for-angular-development-4p12</link>
      <guid>https://dev.to/lxt/agentic-engineering-how-to-set-up-your-harness-for-angular-development-4p12</guid>
      <description>&lt;h1&gt;
  
  
  Agentic Engineering: How to Set Up Your Harness for Angular Development
&lt;/h1&gt;

&lt;p&gt;Over the last few weeks (starting in late May 2026) I've written about which &lt;a href="https://www.angulararchitects.io/blog/best-llms-for-angular/" rel="noopener noreferrer"&gt;LLM is best for &lt;em&gt;Angular&lt;/em&gt; development&lt;/a&gt; and, right after, about which &lt;a href="https://www.angulararchitects.io/blog/ai-apps-harnesses-for-angular/" rel="noopener noreferrer"&gt;app or harness&lt;/a&gt; to use around it. In that second post I treated the words &lt;em&gt;app&lt;/em&gt; and &lt;em&gt;harness&lt;/em&gt; as more or less the same thing and never really drew a line between them. This post is the follow-up that does exactly that.&lt;/p&gt;

&lt;p&gt;It zooms in on the &lt;strong&gt;harness&lt;/strong&gt;: what it actually is, and how to set one up for your &lt;em&gt;Angular&lt;/em&gt; projects so the agent not only runs smoothly, but produces generated and refactored code that I would actually accept in long-lived, continuously evolving &lt;em&gt;Angular&lt;/em&gt; codebases.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR: The Harness Is Where Your Quality Lives
&lt;/h2&gt;

&lt;p&gt;If you only have thirty seconds: the &lt;strong&gt;harness&lt;/strong&gt; is the software around the model – it gives the LLM its tools and runs the agentic loop, and it is the layer &lt;em&gt;you&lt;/em&gt; control. Set it up in five steps: a clean baseline, formatting and linting that run on every commit, your own &lt;em&gt;Angular&lt;/em&gt; style guide (the &lt;strong&gt;single highest-leverage file&lt;/strong&gt; in the whole setup), an &lt;code&gt;AGENTS.md&lt;/code&gt; operating contract, and thin config files that wire every agent back to the same rules. Then give the agent fast feedback loops, from &lt;code&gt;ng lint&lt;/code&gt; all the way to Playwright. Everything below lives in the companion repo &lt;a href="https://github.com/L-X-T/ng-agentic" rel="noopener noreferrer"&gt;ng-agentic&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  So What Is the Harness?
&lt;/h2&gt;

&lt;p&gt;Small naming note for &lt;em&gt;Angular&lt;/em&gt; readers: I do &lt;strong&gt;not&lt;/strong&gt; mean the &lt;a href="https://angular.dev/guide/testing/using-component-harnesses" rel="noopener noreferrer"&gt;Angular CDK &lt;code&gt;ComponentHarness&lt;/code&gt;&lt;/a&gt; for testing components. In this post, a harness is the software around the model.&lt;/p&gt;

&lt;p&gt;The harness is the thing that contains the model. And here's what a lot of people miss: on its own, even a strong LLM is basically just fancy autocomplete, like the one we've been using for years from GitHub Copilot. The only thing it can really do is respond to our messages by writing text. So how is it possible that it can edit our code at all?&lt;/p&gt;

&lt;p&gt;This is where the harness comes in. It gives the model the power of &lt;strong&gt;tool calling&lt;/strong&gt;, and suddenly that autocomplete can do real work: list the current directory, search the whole project for &lt;code&gt;inject(&lt;/code&gt;, open a file, apply an edit, run &lt;code&gt;ng build&lt;/code&gt;. Instead of only writing text back to us, the model emits a structured tool call, the harness actually runs it, and then feeds the result back into the conversation. (Simon Willison has a really nice, no-nonsense walk-through of &lt;a href="https://simonwillison.net/guides/agentic-engineering-patterns/how-coding-agents-work/" rel="noopener noreferrer"&gt;how coding agents work&lt;/a&gt; if you want to go one level deeper.) So in short: the harness is the wrapper around the model that lets it read our projects and then actually change them.&lt;/p&gt;

&lt;p&gt;And that feedback loop is the whole trick. A coding agent is really just our LLM running in a &lt;strong&gt;loop&lt;/strong&gt;: it proposes an action, the harness runs the tool and hands the output back, the model reads that output and decides what to do next – open another file, fix the error it just caused, run the tests again. This loop is what most people mean today when they talk about &lt;a href="https://en.wikipedia.org/wiki/AI_agent" rel="noopener noreferrer"&gt;AI agents&lt;/a&gt;: the harness manages the context, the tools, the prompts, and the control flow, while the model does the actual reasoning.&lt;/p&gt;

&lt;p&gt;If that loop feels like a big jump from the autocomplete days, that's because it is – but we got here in small steps. First we copy-pasted code into ChatGPT and pasted the answers back, being the loop ourselves. Then the chat moved into our IDEs, right next to the code. Then came tool calling, and the wrapper could suddenly act instead of just talk. And today the harness runs the whole loop for us. The models improved along the way, sure – but every one of those steps was a harness step.&lt;/p&gt;

&lt;p&gt;My personal take? The harness, not the model, is where most of the day-to-day quality comes from. Two developers can run the exact same Opus or GPT model and still get wildly different results, simply because one harness has the right tools, the right project context, and sensible guardrails – and the other doesn't. And that's exactly why this post exists: the harness is the part &lt;em&gt;you&lt;/em&gt; control, so setting it up properly for &lt;em&gt;Angular&lt;/em&gt; is not some nice-to-have. In my opinion, it's the highest-leverage thing you can do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Model, Harness, App – What's the Difference?
&lt;/h2&gt;

&lt;p&gt;We now know what a harness does – but it's easy to confuse it with the model underneath it and the app around it. So let me make that concrete with the exact setup I've been using for &lt;em&gt;Angular&lt;/em&gt; development today:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The model (our LLM):&lt;/strong&gt; &lt;a href="https://www.anthropic.com/news/claude-opus-4-8" rel="noopener noreferrer"&gt;Opus&lt;/a&gt; – the brain (BTW, I hope we get &lt;a href="https://www.angulararchitects.io/blog/ai-next-gen-model/" rel="noopener noreferrer"&gt;Fable 5&lt;/a&gt; back soon 😏).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The harness:&lt;/strong&gt; &lt;a href="https://www.anthropic.com/product/claude-code" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt; – gives Opus its tools and runs the loop we just talked about.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The app:&lt;/strong&gt; the &lt;strong&gt;&lt;a href="https://claude.ai/download" rel="noopener noreferrer"&gt;Claude Desktop&lt;/a&gt;&lt;/strong&gt; app – the window I happen to be looking at all of this through.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's the part that trips a lot of people up: we can swap the &lt;strong&gt;app&lt;/strong&gt; without touching the harness or the model at all. The exact same Claude Code + Opus combination can live in a bunch of different places:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;in the &lt;strong&gt;&lt;a href="https://claude.ai/download" rel="noopener noreferrer"&gt;Claude Desktop&lt;/a&gt;&lt;/strong&gt; app,&lt;/li&gt;
&lt;li&gt;in the &lt;strong&gt;&lt;a href="https://www.anthropic.com/product/claude-code" rel="noopener noreferrer"&gt;Claude Code CLI&lt;/a&gt;&lt;/strong&gt;, right in our terminal – historically its native home,&lt;/li&gt;
&lt;li&gt;as the official &lt;strong&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=Anthropic.claude-code" rel="noopener noreferrer"&gt;Claude extension in VS Code&lt;/a&gt;&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;as the official &lt;strong&gt;Claude plugin in &lt;a href="https://www.jetbrains.com/webstorm/" rel="noopener noreferrer"&gt;WebStorm&lt;/a&gt;&lt;/strong&gt; (and the other JetBrains IDEs),&lt;/li&gt;
&lt;li&gt;and even inside &lt;strong&gt;&lt;a href="https://cursor.com/" rel="noopener noreferrer"&gt;Cursor&lt;/a&gt;&lt;/strong&gt; – whose extension is basically the same one we use in VS Code, since Cursor is a VS Code fork.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Same brain, same harness, five different windows. The app is mostly just the UI we sit in front of. BTW, everything we said about the Claude Code harness is also true for Codex. You can use it in the Codex app, in the terminal directly, or in an extension in VS Code, JetBrains IDEs, or Cursor.&lt;/p&gt;

&lt;p&gt;Now it gets interesting. Inside &lt;strong&gt;Cursor&lt;/strong&gt;, we don't have to use the Claude extension at all – we can switch to &lt;strong&gt;Cursor's own harness&lt;/strong&gt; instead, still running Opus underneath. So suddenly we have one app and one model, but &lt;strong&gt;two different harnesses&lt;/strong&gt; to choose from. That's the cleanest way I know to actually &lt;em&gt;feel&lt;/em&gt; the difference: nothing changed except the harness.&lt;/p&gt;

&lt;p&gt;And harnesses are not created equal. In my experience – and in some benchmark results – Cursor's own harness can squeeze noticeably more out of the same Opus model than the Claude extension does: better scores, smoother experience. The gap can be surprisingly large: in an independent benchmark cited in &lt;a href="https://thoughts.jock.pl/p/ai-coding-harness-agents-2026" rel="noopener noreferrer"&gt;Paweł Józefiak's harness comparison&lt;/a&gt;, the same planning task with the same model went from &lt;strong&gt;77%&lt;/strong&gt; running in Claude Code to &lt;strong&gt;93%&lt;/strong&gt; in Cursor – nothing changed but the harness. That does not mean Cursor wins every harness benchmark; the Artificial Analysis chart below shows a different picture for a different Opus comparison. The point is narrower and more important: the wrapper changes the result.&lt;/p&gt;

&lt;p&gt;And the reason isn't magic, it's tuning: a team like Cursor's pours real effort into shaping its tools, system prompt, and tool descriptions for each new model, and that hand-tuning is what wrings the extra points out. So why would anyone still pick the Claude extension inside Cursor? One big reason: &lt;strong&gt;cost&lt;/strong&gt;. With the Claude extension you can run Opus on your existing Claude subscription, while Cursor's harness bills you per API token. For heavy daily &lt;em&gt;Angular&lt;/em&gt; work, that's the difference between a flat monthly fee and a frankly scary API bill.&lt;/p&gt;

&lt;p&gt;So that's the whole picture: the &lt;strong&gt;model&lt;/strong&gt; is the brain, the &lt;strong&gt;harness&lt;/strong&gt; is what gives it hands and runs the loop, and the &lt;strong&gt;app&lt;/strong&gt; is just the window we watch it through. We can mix and match them surprisingly freely – and as the rest of this post will show, the harness is the layer that's really worth getting right.&lt;/p&gt;

&lt;h3&gt;
  
  
  When the Claude Code Harness Leaked
&lt;/h3&gt;

&lt;p&gt;If you ever doubted that the harness is its own engineering layer, April 2026 gave us an accidental case study. A Claude Code npm package &lt;a href="https://venturebeat.com/technology/claude-codes-source-code-appears-to-have-leaked-heres-what-we-know" rel="noopener noreferrer"&gt;included source maps that exposed a large part of the TypeScript source&lt;/a&gt;, so developers could inspect the harness from the inside.&lt;/p&gt;

&lt;p&gt;And what was visible? No secret model, no hidden API – mostly the harness. The &lt;a href="https://nodesource.com/blog/anthropic-claude-code-source-leak-bun-bug" rel="noopener noreferrer"&gt;leaked code exposed&lt;/a&gt; Claude Code's system prompt, the definitions and logic for its tools (bash, read, write, edit, glob, grep), the shape of its agentic loop, and how it pulls in &lt;code&gt;CLAUDE.md&lt;/code&gt;. In other words: exactly the stuff we've been calling "the harness" all along. Anthropic's public statement framed it as a release packaging issue rather than a customer-data breach.&lt;/p&gt;

&lt;p&gt;For me, the fascinating part is what it confirmed: a huge amount of what makes Claude Code feel so capable lives in the system prompt and the tool descriptions, not only in the model weights. That's the harness doing its job – and a pretty good reminder that this layer is something real, inspectable, and very much worth getting right.&lt;/p&gt;

&lt;h2&gt;
  
  
  So Which Harness Is Best for Angular?
&lt;/h2&gt;

&lt;p&gt;So if the harness is the layer worth getting right, the obvious next question is: which one should we actually use for &lt;em&gt;Angular&lt;/em&gt;? Short answer: I can't prove it. There's no benchmark that measures the "best harness for &lt;em&gt;Angular&lt;/em&gt;" specifically, and honestly I don't think there could be one – so everything in this section is my personal, subjective verdict, same as the rest of this series.&lt;/p&gt;

&lt;p&gt;That said, there is one place we &lt;em&gt;can&lt;/em&gt; compare setups with the model held fixed. The &lt;a href="https://artificialanalysis.ai/agents/coding-agents" rel="noopener noreferrer"&gt;Artificial Analysis Coding Agent Index&lt;/a&gt; is one of the few benchmarks where that's actually possible – for example, here is the same Opus model at medium effort running through opencode, Cursor CLI, and Claude Code:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://artificialanalysis.ai/agents/coding-agents#harness-comparison" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnxyie71whk63w2ly18n0.png" alt="Harness comparison by Artificial Analysis" width="799" height="466"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Harness comparison by Artificial Analysis for the same Opus model at medium effort across opencode, Cursor CLI, and Claude Code – click the chart for the &lt;a href="https://artificialanalysis.ai/agents/coding-agents#harness-comparison" rel="noopener noreferrer"&gt;live comparison&lt;/a&gt;. That's exactly what makes it a &lt;em&gt;harness&lt;/em&gt; benchmark and not just another model leaderboard.&lt;/p&gt;

&lt;h3&gt;
  
  
  In the Terminal
&lt;/h3&gt;

&lt;p&gt;If you live in the &lt;strong&gt;terminal&lt;/strong&gt;, here are the harnesses worth knowing about, roughly in the order I'd reach for them today:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/sst/opencode" rel="noopener noreferrer"&gt;opencode&lt;/a&gt;&lt;/strong&gt; – if you only want one recommendation, start here. It's the open-source community favorite – a polished, terminal-native harness with a huge user base, and it's just as bring-your-own-key and model-agnostic as the rest, so you can point it at Opus, GPT, Gemini or even a local model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/earendil-works/pi" rel="noopener noreferrer"&gt;Pi&lt;/a&gt;&lt;/strong&gt; – the alternative for the tinkerers among us. A refreshingly minimal harness built around a tiny core that you extend yourself with TypeScript extensions and skills. Lovely if you enjoy reshaping your tools; probably overkill if you just want to get coding.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.anthropic.com/product/claude-code" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt;&lt;/strong&gt; – a few months ago, this was the one to beat: one of the best harnesses &lt;em&gt;and&lt;/em&gt; one of the best overall experiences you could get in the terminal, full stop. Honestly, though, I wouldn't reach for it there anymore. It's the same harness that now powers the Claude Desktop app, and the experience inside that app – and the other super apps – has become so much better that the bare CLI just doesn't feel worth it today.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Super Apps
&lt;/h3&gt;

&lt;p&gt;Personally, though, I keep coming back to the &lt;strong&gt;super apps&lt;/strong&gt;: &lt;strong&gt;Codex&lt;/strong&gt;, the &lt;strong&gt;Claude Desktop&lt;/strong&gt; app, and &lt;strong&gt;Cursor&lt;/strong&gt;. As I wrote in the &lt;a href="https://www.angulararchitects.io/blog/ai-apps-harnesses-for-angular/" rel="noopener noreferrer"&gt;apps and harnesses post&lt;/a&gt;, these three give me the biggest, most polished feature set – and they happen to ship really good harnesses, too. As an app, I personally like &lt;strong&gt;Codex&lt;/strong&gt; the most.&lt;/p&gt;

&lt;p&gt;So where does that leave us? All three of my go-to super apps (&lt;strong&gt;&lt;a href="https://openai.com/codex/" rel="noopener noreferrer"&gt;Codex&lt;/a&gt;&lt;/strong&gt;, the &lt;strong&gt;&lt;a href="https://claude.ai/download" rel="noopener noreferrer"&gt;Claude Desktop&lt;/a&gt;&lt;/strong&gt; app and &lt;strong&gt;&lt;a href="https://cursor.com/" rel="noopener noreferrer"&gt;Cursor&lt;/a&gt;&lt;/strong&gt;) are genuinely good choices. For me it mostly comes down to whether I want &lt;strong&gt;GPT&lt;/strong&gt; or &lt;strong&gt;Opus&lt;/strong&gt; doing the work – and whether I feel like switching between them depending on the task. If you like to switch, you can either juggle several apps and harnesses (what I usually do), or just use &lt;strong&gt;Cursor for everything&lt;/strong&gt;, since it happily runs all the major models under its own harness.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inside Your IDE
&lt;/h3&gt;

&lt;p&gt;Not everyone wants to leave their editor, though. If you'd rather keep the harness &lt;em&gt;inside&lt;/em&gt; your IDE, &lt;strong&gt;&lt;a href="https://cline.bot/" rel="noopener noreferrer"&gt;Cline&lt;/a&gt;&lt;/strong&gt; is the one I'd point you to: an open-source, model-agnostic extension that drops into VS Code (and its forks, plus JetBrains) and runs whatever model you bring – Opus, GPT, Gemini, or a local one. Same bring-your-own-key, vendor-neutral spirit as opencode, just living in your editor instead of the terminal.&lt;/p&gt;

&lt;p&gt;And here's the part I didn't see coming: you don't always have to pay per API token. Cline can run on the &lt;strong&gt;subscriptions you already have&lt;/strong&gt; instead of metered API billing – sign in with your OpenAI account to use your &lt;strong&gt;&lt;a href="https://cline.bot/blog/introducing-openai-codex-oauth" rel="noopener noreferrer"&gt;ChatGPT&lt;/a&gt;&lt;/strong&gt; plan through the OpenAI Codex provider, or point it at a locally authenticated &lt;strong&gt;&lt;a href="https://docs.cline.bot/provider-config/claude-code" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt;&lt;/strong&gt; CLI to bring your &lt;strong&gt;Claude&lt;/strong&gt; plan. Either way it's the same flat-fee-not-scary-bill advantage I made a fuss about above, now inside your editor. (The Claude path trades away token-by-token streaming and some prompt caching for it, but for daily &lt;em&gt;Angular&lt;/em&gt; work that's an easy deal.)&lt;/p&gt;

&lt;p&gt;One setup I'd gently steer you &lt;em&gt;away&lt;/em&gt; from, even though it's still the most common one I see: plain &lt;strong&gt;VS Code with GitHub Copilot&lt;/strong&gt;. The GPT model inside is perfectly capable – this isn't a model problem. It's that Copilot grew out of autocomplete, and its agent experience still feels a step behind the purpose-built harnesses to me. Tellingly, even &lt;a href="https://code.visualstudio.com/blogs/2026/05/15/agent-harnesses-github-copilot-vscode" rel="noopener noreferrer"&gt;VS Code's own team&lt;/a&gt; now frames Copilot around "the harness" – &lt;em&gt;"The model is the engine. The harness is the car."&lt;/em&gt; – and says it's where they spend most of their engineering time. They're right. So if your whole AI setup is still VS Code plus Copilot, that's exactly the setup worth outgrowing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fourth Factor: Your Codebase
&lt;/h2&gt;

&lt;p&gt;So far this post has been about three things you assemble: the &lt;strong&gt;model&lt;/strong&gt;, the &lt;strong&gt;harness&lt;/strong&gt;, and the &lt;strong&gt;app&lt;/strong&gt;. But there's a fourth factor that quietly shapes everything the agent produces, and it's the one you can't pick from a menu: &lt;strong&gt;your existing codebase&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here's why it matters so much. An agent doesn't write &lt;em&gt;Angular&lt;/em&gt; in a vacuum – it reads the code around the change first, and then it imitates. Give it a clean, modern, consistent codebase and it has the best possible reference: it sees your real components, your signal stores, your services, and writes new code that looks like what's already there. In a codebase like that, honestly, the harness setup matters &lt;em&gt;less&lt;/em&gt; – the style guide, the &lt;code&gt;AGENTS.md&lt;/code&gt;, the lint rules are still worth having, but the code itself is already doing half the teaching.&lt;/p&gt;

&lt;p&gt;Now flip it. Point the same agent at a legacy codebase – &lt;code&gt;any&lt;/code&gt; everywhere, &lt;code&gt;NgModule&lt;/code&gt;s, &lt;code&gt;*ngIf&lt;/code&gt;, RxJS where signals belong, no strict typing, a barely-there ESLint config – and it imitates &lt;em&gt;that&lt;/em&gt; instead. It will happily match the mess, because the mess is what it sees. This is where the setup stops being a nice-to-have and becomes the thing holding the line: a strong style guide, strict TypeScript, a serious ESLint rule set, and an opinionated &lt;code&gt;AGENTS.md&lt;/code&gt; are what override the bad examples in the surrounding code and pull the output back up to the standard you actually want.&lt;/p&gt;

&lt;p&gt;So there's an inverse relationship worth internalizing: &lt;strong&gt;the worse your codebase, the more your harness setup matters.&lt;/strong&gt; On a pristine greenfield project you can get away with a light touch. On the brownfield codebases most of us actually work in, the guardrails and the style guide aren't decoration – they're the only thing between you and an agent that cheerfully writes 2019-era &lt;em&gt;Angular&lt;/em&gt; because that's what it found lying around.&lt;/p&gt;

&lt;p&gt;With a harness picked – and a clear-eyed sense of the codebase you're aiming it at – the real work only starts: actually setting it up so it writes &lt;em&gt;Angular&lt;/em&gt; the way we want. That's what the rest of this post is about.&lt;/p&gt;

&lt;h2&gt;
  
  
  So How Do I Actually Set This Up?
&lt;/h2&gt;

&lt;p&gt;Enough theory – let me show you the exact setup I use. Everything below lives in a companion repo, &lt;strong&gt;&lt;a href="https://github.com/L-X-T/ng-agentic" rel="noopener noreferrer"&gt;ng-agentic&lt;/a&gt;&lt;/strong&gt;: a small &lt;em&gt;Angular&lt;/em&gt; workspace I generated fresh with the &lt;strong&gt;Angular CLI 22&lt;/strong&gt; and then turned, commit by commit, into something every agent can work in. Clone it, read through the history, or just follow along here and build the same thing into your own project – either way, the goal is one workspace where the harness produces the &lt;em&gt;Angular&lt;/em&gt; code we actually want.&lt;/p&gt;

&lt;p&gt;At a high level, the setup has five parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;start from a clean CLI baseline, or identify the brownfield baseline you already have&lt;/li&gt;
&lt;li&gt;make formatting and linting non-negotiable&lt;/li&gt;
&lt;li&gt;hand the agent your &lt;em&gt;Angular&lt;/em&gt; style guide&lt;/li&gt;
&lt;li&gt;turn &lt;code&gt;AGENTS.md&lt;/code&gt; into an operating contract&lt;/li&gt;
&lt;li&gt;wire every app, IDE, and agent back to the same rules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That greenfield-or-brownfield choice shows up immediately, at the very first command. I went &lt;strong&gt;greenfield&lt;/strong&gt; for this repo – a fresh &lt;code&gt;ng new&lt;/code&gt; – because it makes every step easy to show on its own. But let's be honest: most of us aren't starting from scratch. We're dropping the agent into an existing &lt;em&gt;Angular&lt;/em&gt; app – &lt;strong&gt;brownfield&lt;/strong&gt;, legacy or not – the more common case by far. The good news is that almost everything below is identical either way. The only greenfield-only step is the &lt;code&gt;ng new&lt;/code&gt; itself – on an existing project you simply skip it and layer the same tooling and conventions on top of what you already have.&lt;/p&gt;

&lt;h3&gt;
  
  
  Start from a Clean &lt;code&gt;ng new&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The greenfield path is the boring one – a fresh project straight from the &lt;strong&gt;&lt;a href="https://github.com/angular/angular-cli" rel="noopener noreferrer"&gt;Angular CLI&lt;/a&gt;&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng new ng-agentic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That gives us the usual scaffold – config, dependencies, a starter component, an initialized Git repo. The CLI can also generate agent config for us along the way – either when &lt;code&gt;ng new&lt;/code&gt; asks which AI tools we use, or later via &lt;code&gt;ng generate ai-config&lt;/code&gt; – and that's where the first &lt;code&gt;AGENTS.md&lt;/code&gt; comes from, the file every agent reads first. We'll make that file do real work further down; for now it just needs to exist.&lt;/p&gt;

&lt;p&gt;The only cleanup on that first commit was trimming a few duplicated instructions out of the generated &lt;code&gt;AGENTS.md&lt;/code&gt; and running the formatter over everything:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm format   &lt;span class="c"&gt;# runs prettier --write .&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Doing that by hand on every commit gets old fast – which is exactly the next thing to automate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quality Gates: Make Formatting and Linting Non-Negotiable
&lt;/h3&gt;

&lt;p&gt;Here's the thing about handing an agent the keys: it'll write code in whatever style the prompt nudges it toward, and it'll just as happily commit a half-formatted mess if nothing stops it. So before I teach the harness &lt;em&gt;how&lt;/em&gt; I want my &lt;em&gt;Angular&lt;/em&gt; written, I give it a hard floor it can't sink below – formatting and linting that run automatically on every commit, whether the code came from me or from the model.&lt;/p&gt;

&lt;p&gt;The enforcer is a &lt;strong&gt;&lt;a href="https://typicode.github.io/husky/" rel="noopener noreferrer"&gt;Husky&lt;/a&gt;&lt;/strong&gt; pre-commit hook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm add &lt;span class="nt"&gt;-D&lt;/span&gt; husky
pnpm &lt;span class="nb"&gt;exec &lt;/span&gt;husky init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;husky init&lt;/code&gt; creates &lt;code&gt;.husky/pre-commit&lt;/code&gt;; I point it at the project's format script, so every commit gets &lt;code&gt;prettier&lt;/code&gt;-formatted before it lands. No more "fix formatting" commits, and no more arguing with the agent about quotes and trailing commas.&lt;/p&gt;

&lt;p&gt;Formatting is the easy half. For real code quality I add &lt;strong&gt;&lt;a href="https://github.com/angular-eslint/angular-eslint" rel="noopener noreferrer"&gt;Angular ESLint&lt;/a&gt;&lt;/strong&gt;, which understands &lt;em&gt;Angular&lt;/em&gt; and not just plain TypeScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng add @angular-eslint/schematics
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Linting the whole project on every commit is slow, so I scope it to the staged files with &lt;strong&gt;lint-staged&lt;/strong&gt; and run that from the hook instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"lint-staged"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"*.{html,js,ts}"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"eslint --fix"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"prettier --write"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"*.{css,json,md,scss}"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"prettier --write"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The default &lt;em&gt;Angular&lt;/em&gt; ESLint preset is fairly relaxed, so I tighten &lt;a href="https://github.com/L-X-T/ng-agentic/blob/main/eslint.config.js" rel="noopener noreferrer"&gt;&lt;code&gt;eslint.config.js&lt;/code&gt;&lt;/a&gt; with the stricter rules I actually care about, then do a one-time cleanup pass with &lt;code&gt;ng lint --fix&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And here's a small moment that captures the whole point of this post. After the auto-fix, two issues are left that ESLint won't touch on its own – a component missing &lt;code&gt;OnPush&lt;/code&gt;, and a function missing its return type. Both are mechanical, so I don't fix them by hand: I hand them to the agent and let the harness close its own loop. The guardrail flags the problem, the model fixes it, and the hook verifies the fix on the way back in.&lt;/p&gt;

&lt;p&gt;One word for the &lt;strong&gt;brownfield&lt;/strong&gt; crowd: that first &lt;code&gt;ng lint&lt;/code&gt; won't print two warnings, it'll print a few hundred. Don't &lt;code&gt;--fix&lt;/code&gt; the whole pile in one heroic commit – treat that wall as your honest baseline, the real starting state of the code, and chip away at it deliberately. It's exactly the kind of backlog the agent is good at later, but that's a story for another post.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hand the Agent Your Style Guide
&lt;/h3&gt;

&lt;p&gt;If you take only one thing from this entire post, take this one. &lt;strong&gt;The style guide is the single highest-leverage file in the whole setup&lt;/strong&gt; – it matters more than the linting, more than the MCP servers, more than the clever &lt;code&gt;AGENTS.md&lt;/code&gt; we'll get to next.&lt;/p&gt;

&lt;p&gt;Here's why. A strong model already knows &lt;em&gt;Angular&lt;/em&gt; – generically. Ask it for a component and you'll get a perfectly reasonable, perfectly &lt;em&gt;generic&lt;/em&gt; one: maybe Jest where you use &lt;strong&gt;Vitest&lt;/strong&gt;, maybe half-remembered &lt;code&gt;NgModule&lt;/code&gt;s from 2021, maybe RxJS where you'd reach for a signal. What the model &lt;em&gt;doesn't&lt;/em&gt; know is how &lt;strong&gt;your&lt;/strong&gt; team writes &lt;em&gt;Angular&lt;/em&gt; – your stack, your conventions, your taste. The style guide is where you hand it exactly that. And the moment you do, the harness stops writing generic &lt;em&gt;Angular&lt;/em&gt; and starts writing &lt;em&gt;yours&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In the repo this lives in a &lt;a href="https://github.com/L-X-T/ng-agentic/tree/main/style-guide" rel="noopener noreferrer"&gt;&lt;code&gt;style-guide/&lt;/code&gt;&lt;/a&gt; folder. One general &lt;a href="https://github.com/L-X-T/ng-agentic/blob/main/style-guide/style-guide.md" rel="noopener noreferrer"&gt;&lt;code&gt;style-guide.md&lt;/code&gt;&lt;/a&gt; sets the baseline – it pins the target stack (&lt;strong&gt;Angular v22+&lt;/strong&gt;, &lt;strong&gt;TypeScript 6&lt;/strong&gt;, &lt;strong&gt;zoneless&lt;/strong&gt;, &lt;strong&gt;Vitest + Playwright&lt;/strong&gt;) and lays out plain &lt;strong&gt;Do / Should / Don't&lt;/strong&gt; rules. It's a living document, too – versioned (2.0.1 as I write this), not a one-off.&lt;/p&gt;

&lt;p&gt;Around that baseline sit focused, file-type-specific guides. Start with the &lt;a href="https://github.com/L-X-T/ng-agentic/blob/main/style-guide/style-guide.md" rel="noopener noreferrer"&gt;Angular Coding Style Guide&lt;/a&gt; itself, then jump straight to whichever specific one is relevant:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/L-X-T/ng-agentic/blob/main/style-guide/style-guide.ts.md" rel="noopener noreferrer"&gt;TypeScript (Angular) files&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/L-X-T/ng-agentic/blob/main/style-guide/style-guide.html.md" rel="noopener noreferrer"&gt;HTML view templates&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/L-X-T/ng-agentic/blob/main/style-guide/style-guide.scss.md" rel="noopener noreferrer"&gt;SCSS styling files&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/L-X-T/ng-agentic/blob/main/style-guide/style-guide.a11y.md" rel="noopener noreferrer"&gt;Accessibility (a11y)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/L-X-T/ng-agentic/blob/main/style-guide/style-guide.spec.md" rel="noopener noreferrer"&gt;Testing (Vitest &amp;amp; Playwright)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/L-X-T/ng-agentic/blob/main/style-guide/style-guide.git.md" rel="noopener noreferrer"&gt;Git commits&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/L-X-T/ng-agentic/blob/main/style-guide/style-guide.md.md" rel="noopener noreferrer"&gt;Markdown files&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/L-X-T/ng-agentic/blob/main/style-guide/style-guide.npm.md" rel="noopener noreferrer"&gt;NPM packages&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two design choices in here are worth copying:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It's written for humans &lt;em&gt;and&lt;/em&gt; agents at once.&lt;/strong&gt; Same document, one source of truth. A new teammate reads it on day one; the agent reads it on every task. No second set of conventions quietly drifting out of sync.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's split by file type on purpose.&lt;/strong&gt; When the agent edits a template, it should pull in the HTML guide – not the SCSS rules, not the testing checklist, not the entire wall of conventions at once. This isn't just tidiness: a model's accuracy actually drops as its context fills up, and burying the one rule that matters among hundreds that don't is the worst &lt;em&gt;needle-in-a-haystack&lt;/em&gt; problem you can hand it. Picture fixing a bug when you're shown the two files it might live in versus all two thousand. Narrower, relevant context means sharper focus and better output, and it pays off more and more as the guide grows.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So this is the section to spend real time on. Borrow the structure from the repo if you like, but fill it with &lt;em&gt;your&lt;/em&gt; rules – the ones you'd give a sharp new hire in their first week. That document is what turns a capable-but-generic harness into one that writes &lt;em&gt;Angular&lt;/em&gt; the way your codebase actually wants it written.&lt;/p&gt;

&lt;p&gt;And mine is genuinely never finished. I keep refining this style guide as &lt;em&gt;Angular&lt;/em&gt; moves and as I learn what actually holds up in practice – so if you spot something missing, something you'd do differently, or a rule that has aged badly, I'd honestly love to hear it. Open an issue or a pull request on the &lt;a href="https://github.com/L-X-T/ng-agentic" rel="noopener noreferrer"&gt;repo&lt;/a&gt;, or just reach out and tell me. Good conventions are a team sport, and this one gets a little better every time someone pushes back on it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Turn AGENTS.md Into an Operating Contract
&lt;/h3&gt;

&lt;p&gt;If the style guide is about how the code should &lt;em&gt;look&lt;/em&gt;, &lt;strong&gt;&lt;code&gt;AGENTS.md&lt;/code&gt; is about how the agent should &lt;em&gt;behave&lt;/em&gt;&lt;/strong&gt; – the rules of engagement it reads before it touches anything. The version the CLI generates is fine, but generic. So I rewrote it into a repo-specific &lt;em&gt;operating contract&lt;/em&gt;: a short document that tells the harness exactly how to act in &lt;em&gt;this&lt;/em&gt; codebase.&lt;/p&gt;

&lt;p&gt;The part I care about most is the workflow rules, because this is where a model goes from "powerful but a bit reckless" to "careful teammate." A few that earn their place:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Preserve my edits.&lt;/strong&gt; Before changing a file, check its current state and don't revert, rewrite, or drop my changes or comments unless I asked. It's the same rule I'd give any human collaborator – and the one I try to hold myself to.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep fixes local.&lt;/strong&gt; When fixing a bug, change the minimum needed. No opportunistic refactors, no "while I'm here" cleanups smuggled into the diff. Spotted a worthwhile refactor? Suggest it; don't sneak it in.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stay in your lane.&lt;/strong&gt; Don't touch unrelated files, prefer minimal diffs, and run lint and the relevant tests before declaring a task done.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't start the dev server.&lt;/strong&gt; Never fire up &lt;code&gt;ng serve&lt;/code&gt; on its own – I usually already have one running on &lt;code&gt;:4200&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is also where the two layers connect. &lt;strong&gt;&lt;code&gt;AGENTS.md&lt;/code&gt; is the primary instruction – the one file the agent always loads&lt;/strong&gt;, on every task, no exceptions. The style guides are not; they come in through &lt;strong&gt;progressive disclosure&lt;/strong&gt;, just-in-time, only when the agent actually touches a matching file. Create or edit a template and the HTML guide loads; touch a &lt;code&gt;.scss&lt;/code&gt; and the styling guide loads; nothing else competes for the model's attention. &lt;code&gt;AGENTS.md&lt;/code&gt; spells out that contract in one decisive line – read &lt;em&gt;only&lt;/em&gt; the narrowest relevant guide – plus a precedence rule: &lt;strong&gt;project rules override generic &lt;em&gt;Angular&lt;/em&gt; advice and any skill's examples&lt;/strong&gt;. That precedence is what stops a confident model from quietly defaulting to the &lt;em&gt;Angular&lt;/em&gt; it learned two years ago.&lt;/p&gt;

&lt;p&gt;There's a deeper reason this file pulls its weight. An agent starts every session knowing nothing about your repo – and if something isn't in its context, it may as well not exist for the model – so without &lt;code&gt;AGENTS.md&lt;/code&gt; it has to spend tool calls rediscovering how the project is laid out, every single time. &lt;code&gt;AGENTS.md&lt;/code&gt; front-loads just enough to skip that: not the whole codebase, but the always-true orientation – your conventions, where things live, which narrow guide to read next – so the agent starts pointed in the right direction instead of guessing.&lt;/p&gt;

&lt;p&gt;And it bakes in the modern &lt;strong&gt;&lt;em&gt;Angular&lt;/em&gt; v22+&lt;/strong&gt; defaults so I don't have to repeat them in every prompt: standalone and zoneless, signals over decorators (&lt;code&gt;input()&lt;/code&gt;, &lt;code&gt;output()&lt;/code&gt;, &lt;code&gt;computed()&lt;/code&gt;, &lt;code&gt;.set()&lt;/code&gt;/&lt;code&gt;.update()&lt;/code&gt; – never &lt;code&gt;.mutate()&lt;/code&gt;), native &lt;code&gt;@if&lt;/code&gt;/&lt;code&gt;@for&lt;/code&gt; instead of &lt;code&gt;*ngIf&lt;/code&gt;/&lt;code&gt;*ngFor&lt;/code&gt;, &lt;code&gt;resource()&lt;/code&gt;/&lt;code&gt;rxResource()&lt;/code&gt; for data fetching, &lt;code&gt;inject()&lt;/code&gt; over constructor injection, strict TypeScript with &lt;strong&gt;no &lt;code&gt;any&lt;/code&gt;, ever&lt;/strong&gt;, AXE/WCAG AA on every template, and no &lt;code&gt;.spec.ts&lt;/code&gt; files unless a real Vitest or Playwright setup exists. (Always worth mentioning: &lt;code&gt;computed()&lt;/code&gt; is my favorite feature in modern &lt;em&gt;Angular&lt;/em&gt;.)&lt;/p&gt;

&lt;p&gt;One warning from experience, though. The moment you have three layers of rules – &lt;code&gt;AGENTS.md&lt;/code&gt;, the style guides, and the ESLint config – they can quietly start to &lt;strong&gt;contradict each other&lt;/strong&gt;, and a contradiction is worse than no rule at all. If &lt;code&gt;AGENTS.md&lt;/code&gt; says "Signal Forms" while a style guide still shows a Reactive Forms example and ESLint enforces neither, the agent picks one almost at random and burns a round-trip second-guessing itself. So I treat the three as a single system: they have to say the same thing, and wherever a rule &lt;em&gt;can&lt;/em&gt; become an ESLint rule, I make it one – a check the harness actually runs beats a sentence it merely reads. Manfred takes this same principle all the way up to architecture itself – &lt;a href="https://www.angulararchitects.io/en/blog/reliable-angular-architectures-with-ai-assisted-coding/" rel="noopener noreferrer"&gt;turning layer and domain boundaries into checks the agent must satisfy&lt;/a&gt;, with Sheriff and feedback hooks.&lt;/p&gt;

&lt;p&gt;The whole thing fits on a screen or two – you can read &lt;a href="https://github.com/L-X-T/ng-agentic/blob/main/AGENTS.md" rel="noopener noreferrer"&gt;the full &lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/a&gt; in the repo. Steal what fits, drop what doesn't; the point isn't my exact rules, it's that the agent should walk into every task already knowing how your team works instead of guessing.&lt;/p&gt;

&lt;p&gt;A caveat before we wire everything up: this setup is &lt;strong&gt;tuned for a specific model generation&lt;/strong&gt;. The style guide, the &lt;code&gt;AGENTS.md&lt;/code&gt;, and the level of detail in both were developed and tested against &lt;strong&gt;Opus 4.5 to 4.8&lt;/strong&gt; and &lt;strong&gt;GPT-5.2 to 5.5&lt;/strong&gt;. Newer, more intelligent models that were trained on the latest &lt;em&gt;Angular&lt;/em&gt; and TypeScript best practices – think &lt;a href="https://www.angulararchitects.io/blog/ai-next-gen-model/" rel="noopener noreferrer"&gt;Fable 5&lt;/a&gt; or GPT-5.6 – will likely need less steering, and I'll have to reevaluate which of these detailed rules still earn their place and which the models simply internalize by then. So treat all of this as a constantly evolving instruction set, not a finished artifact.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wire Up Every Agent
&lt;/h3&gt;

&lt;p&gt;The last step is pure plumbing, but it's what makes the whole thing portable. Everything so far lives in &lt;code&gt;AGENTS.md&lt;/code&gt; and the style guide, so the final move is simply to make &lt;em&gt;every&lt;/em&gt; tool read them. Each assistant – Claude Code, Codex, Cursor, Cline, Junie, Gemini, Windsurf, GitHub Copilot, plain VS Code – gets a thin little file (&lt;code&gt;.cursorrules&lt;/code&gt;, &lt;code&gt;.clinerules&lt;/code&gt;, &lt;code&gt;.gemini/GEMINI.md&lt;/code&gt;, &lt;code&gt;.github/copilot-instructions.md&lt;/code&gt; and friends) that does nothing but point back to &lt;code&gt;AGENTS.md&lt;/code&gt;. Yes, that includes the ones I'd nudge you away from – if a teammate still reaches for Copilot, it should at least follow the same rules as everyone else. One brain, many front-ends: switch tools and the rules come with you.&lt;/p&gt;

&lt;p&gt;Two more things round it out – and the first one deserves a better introduction than the throwaway sentence I almost gave it. I register the project's &lt;strong&gt;MCP servers&lt;/strong&gt; once in &lt;code&gt;.mcp.json&lt;/code&gt; (Angular CLI, Spartan UI, Chrome DevTools, Figma) so any agent can reach the same extra tools. That sounds like plumbing, but it quietly fixes a model limitation nothing else in this setup touches: the &lt;strong&gt;knowledge cutoff&lt;/strong&gt;. Every model stopped learning at some point in the past – ask it about an &lt;em&gt;Angular&lt;/em&gt; v22 API that shipped after that date and it will confidently improvise something v18-flavored instead. The &lt;a href="https://angular.dev/ai/mcp" rel="noopener noreferrer"&gt;Angular CLI MCP server&lt;/a&gt; closes exactly that gap: it hands the agent live documentation search and curated, current code examples, so it can look up what it cannot know. Think of it as the counterpart to the style guide: the style guide teaches the harness &lt;em&gt;our&lt;/em&gt; conventions, the MCP server keeps it honest about the framework itself.&lt;/p&gt;

&lt;p&gt;Alongside that, I make sure no agent ever reads secrets or environment files – &lt;code&gt;.env&lt;/code&gt;, &lt;code&gt;*.pem&lt;/code&gt;, &lt;code&gt;*.key&lt;/code&gt;, &lt;em&gt;Angular&lt;/em&gt;'s &lt;code&gt;environment*.ts&lt;/code&gt;. Fair warning: there's no &lt;code&gt;.gitignore&lt;/code&gt;-style standard for this yet (ironic, given how nicely &lt;code&gt;AGENTS.md&lt;/code&gt; converged), so this one is per-tool config. For &lt;strong&gt;Claude Code&lt;/strong&gt;, it's &lt;a href="https://code.claude.com/docs/en/settings" rel="noopener noreferrer"&gt;&lt;code&gt;permissions.deny&lt;/code&gt; rules&lt;/a&gt; in &lt;code&gt;.claude/settings.json&lt;/code&gt; – a committable file, so the whole team gets the same policy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"deny"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Read(./.env)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Read(./.env.*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Read(*.pem)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Read(*.key)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Read(./src/environments/**)"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For &lt;strong&gt;Codex&lt;/strong&gt;, it's a &lt;a href="https://developers.openai.com/codex/config-reference" rel="noopener noreferrer"&gt;permissions profile&lt;/a&gt; in &lt;code&gt;.codex/config.toml&lt;/code&gt;, enforced by its sandbox:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="py"&gt;default_permissions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"no-secrets"&lt;/span&gt;

&lt;span class="nn"&gt;[permissions.no-secrets.filesystem]&lt;/span&gt;
&lt;span class="py"&gt;"**/.env"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"deny"&lt;/span&gt;
&lt;span class="py"&gt;"**/.env.*"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"deny"&lt;/span&gt;
&lt;span class="py"&gt;"**/*.pem"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"deny"&lt;/span&gt;
&lt;span class="py"&gt;"**/*.key"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"deny"&lt;/span&gt;
&lt;span class="py"&gt;"**/environments/**"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"deny"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not pretty – the same rule written twice – but these are checks the harness actually enforces, and by now you know how much I prefer those over conventions the agent merely promises to follow. The rest is housekeeping: a &lt;code&gt;.prettierrc.json&lt;/code&gt; rename and an &lt;code&gt;ng:update&lt;/code&gt; script for upgrades. It's all in the &lt;a href="https://github.com/L-X-T/ng-agentic" rel="noopener noreferrer"&gt;repo&lt;/a&gt; if you want the exact files.&lt;/p&gt;

&lt;p&gt;And that's the setup – or at least the static half of it. None of these last pieces are clever on their own, but together they mean the harness behaves the same whether I open Codex today or Cursor tomorrow – and that every one of them writes &lt;em&gt;Angular&lt;/em&gt; the way I actually want. What's still missing is the dynamic half: all these rules tell the agent what good &lt;em&gt;Angular&lt;/em&gt; looks like, but they don't tell it whether the code it just wrote actually works. For that, it needs feedback.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feedback Loops
&lt;/h2&gt;

&lt;p&gt;Remember the loop from the beginning of this post – the agent proposes an action, the harness runs it, the model reads the result and decides what to do next? That loop is only as good as what flows back through it. Everything we've set up so far tells the agent what good &lt;em&gt;Angular&lt;/em&gt; looks like; &lt;strong&gt;feedback loops are how it finds out whether it actually delivered.&lt;/strong&gt; The more of them the agent can run on its own, the less it has to guess – so I hand it several, ordered here from cheap to expensive. (The agent-facing side of these loops – lint and static verification, who owns the dev server, test setup boundaries – is captured in &lt;a href="https://github.com/L-X-T/ng-agentic/blob/main/AGENTS.md" rel="noopener noreferrer"&gt;AGENTS.md&lt;/a&gt;, of course.)&lt;/p&gt;

&lt;p&gt;One thing before we walk through them, because it's what turns these loops from advice into a system: the best feedback is the kind the &lt;strong&gt;harness enforces&lt;/strong&gt;, not the kind the agent merely promises. My &lt;code&gt;AGENTS.md&lt;/code&gt; says "run lint and the relevant tests before declaring a task done" – but that's a sentence the model reads, and a model in a hurry will happily skip it and report success anyway. Most harnesses can make the check deterministic instead. Claude Code, for example, has &lt;a href="https://code.claude.com/docs/en/hooks-guide" rel="noopener noreferrer"&gt;hooks&lt;/a&gt;: I can register a Stop hook that fires whenever the agent believes it's finished, runs &lt;code&gt;ng lint&lt;/code&gt; or the tests, and – if anything fails – blocks the "done" and feeds the errors straight back into the loop. The agent doesn't get to decide it's finished; the check does. Same principle as the ESLint moment earlier – a check the harness actually runs beats a sentence it merely reads – applied at the moment it matters most: right when the model would say "it should work now".&lt;/p&gt;

&lt;h3&gt;
  
  
  Linting
&lt;/h3&gt;

&lt;p&gt;The cheapest loop, and the first one the agent should hit. &lt;code&gt;ng lint&lt;/code&gt; turns all the static rules we configured above into immediate feedback – style, accessibility, and TypeScript issues surface before anything even runs, and the diagnostics flow straight back into the agent's next step. This is the guardrail-fixes-itself moment from the ESLint section, running on every task.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;ng build&lt;/code&gt; answers the bigger question: does the whole &lt;em&gt;Angular&lt;/em&gt; graph still compile? Strict TypeScript, templates, imports, bundling – if any of it broke, the error output hands the agent the exact files and symbols to fix. No guessing, no "it should work now".&lt;/p&gt;

&lt;h3&gt;
  
  
  Serving
&lt;/h3&gt;

&lt;p&gt;The dev server is the live loop: manual checks, browser inspection, debugging. But this one comes with my house rule from &lt;code&gt;AGENTS.md&lt;/code&gt;: &lt;strong&gt;the agent does not start the dev server – I do.&lt;/strong&gt; I want full control of that process, and there's usually one running on &lt;code&gt;:4200&lt;/code&gt; anyway. The agent should use the app that's already running instead of firing up its own.&lt;/p&gt;

&lt;h3&gt;
  
  
  Chrome Debugger
&lt;/h3&gt;

&lt;p&gt;Some tools, including Codex, ship an internal browser the agent can use to debug browser state, console errors, network traffic, and interaction bugs. Honestly, I still prefer my own Chrome in debugging mode: start it with a remote debugging port – the &lt;a href="https://developer.chrome.com/docs/devtools/remote-debugging/local-server" rel="noopener noreferrer"&gt;Chrome DevTools remote debugging docs&lt;/a&gt; explain the setup in more detail – for example &lt;code&gt;open -na "Google Chrome" --args --remote-debugging-port=9222 --user-data-dir=/tmp/chrome-agent-debug&lt;/code&gt; – and then connect Chrome DevTools or the agent's browser tool to &lt;code&gt;http://localhost:9222&lt;/code&gt;. Same spirit as the dev server rule: my browser, my process, my control.&lt;/p&gt;

&lt;h3&gt;
  
  
  E2E Testing
&lt;/h3&gt;

&lt;p&gt;The most expensive loop, and the one that catches what all the others miss: real user flows in a real browser. When a Playwright (or Cypress) run fails, the screenshots, traces, logs, and failure messages are exactly the kind of focused feedback an agent can use to repair the feature – much better than me describing the bug in prose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agentic Engineering Workshop
&lt;/h2&gt;

&lt;p&gt;Everything in this post – the guardrails, the &lt;em&gt;Angular&lt;/em&gt; Coding Style Guide, the opinionated &lt;code&gt;AGENTS.md&lt;/code&gt;, and the work of keeping all three in sync – is exactly the kind of setup we build together, hands-on.&lt;/p&gt;

&lt;p&gt;If you want to build this kind of setup for your own &lt;em&gt;Angular&lt;/em&gt; projects – an AI-ready workspace, real guardrails, and a strict human-in-the-loop workflow where every generated line still looks handcrafted – join our &lt;strong&gt;Agentic Engineering Workshop&lt;/strong&gt;, available in English and German.&lt;/p&gt;

&lt;p&gt;In the workshop we take exactly this foundation further: spec-first and plan-first workflows, UX and component prototyping, code review, testing, and brownfield refactoring – moving advanced &lt;em&gt;Angular&lt;/em&gt; teams from vibe coding to traceable &lt;strong&gt;Agentic Software Engineering&lt;/strong&gt; workflows.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🤖 &lt;a href="https://www.angulararchitects.io/en/training/agentic-engineering-von-vibe-coding-zu-professionellen-ki-gestuetzten-workflows/" rel="noopener noreferrer"&gt;&lt;strong&gt;Agentic Engineering Workshop&lt;/strong&gt;&lt;/a&gt; – 2 days, remote&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;If there's one line to take away, it's the same idea this post started with: &lt;strong&gt;the harness, not the model, is where your day-to-day &lt;em&gt;Angular&lt;/em&gt; quality comes from.&lt;/strong&gt; The model will keep getting better whether you do anything or not – that part is out of your hands, and as models internalize current &lt;em&gt;Angular&lt;/em&gt; best practices, parts of this setup will get lighter, not heavier. The harness is the part &lt;em&gt;you&lt;/em&gt; control, and it's what decides whether a strong model writes generic &lt;em&gt;Angular&lt;/em&gt; or writes &lt;em&gt;yours&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;And setting it up isn't mysterious. A clean baseline, formatting and linting that run themselves, a style guide that actually encodes your taste, an opinionated &lt;code&gt;AGENTS.md&lt;/code&gt;, and every tool wired back to the same rules – that's the whole game. If you invest in only one piece, make it the &lt;strong&gt;style guide&lt;/strong&gt;: it's the cheapest, highest-impact way to turn a capable-but-generic harness into one that feels like a teammate who already knows your codebase.&lt;/p&gt;

&lt;p&gt;One honest note on the human-in-the-loop part, because attentive readers of the &lt;a href="https://www.angulararchitects.io/blog/ai-next-gen-model/" rel="noopener noreferrer"&gt;Fable 5 post&lt;/a&gt; might sense a tension here: yes, I wrote there that human review might have an expiry date, and I stand by that. For now, though, reviewing every diff is still necessary – and here's the nice thing: this setup doesn't care who the reviewer is. Guardrails, style guide, and feedback loops make the diff better &lt;em&gt;before&lt;/em&gt; anyone looks at it, and they stay just as valuable on the day a stronger model takes over more of the reviewing from me.&lt;/p&gt;

&lt;p&gt;The best part is that none of this is locked to a vendor. The same &lt;code&gt;AGENTS.md&lt;/code&gt; and style guide work whether I'm in Codex today, Cursor tomorrow, or some app that doesn't exist yet – so when the next model or the next super app shows up, I just point it at the setup I already have. That's exactly why I'd rather invest in the harness than chase the model leaderboard.&lt;/p&gt;

&lt;p&gt;Everything here lives in the &lt;strong&gt;&lt;a href="https://github.com/L-X-T/ng-agentic" rel="noopener noreferrer"&gt;ng-agentic&lt;/a&gt;&lt;/strong&gt; repo – clone it, steal what fits, and make it yours. And this is really just the foundation: in upcoming posts I'll build on top of it with reusable &lt;strong&gt;agent skills&lt;/strong&gt;, the &lt;strong&gt;workflows&lt;/strong&gt; that decide how much structure a task needs, and the part I'm most excited about – using all of this for real &lt;strong&gt;brownfield refactoring&lt;/strong&gt; of dusty &lt;em&gt;Angular&lt;/em&gt; code.&lt;/p&gt;

&lt;p&gt;And if you want to dive deeper on the harness as its own discipline – beyond &lt;em&gt;Angular&lt;/em&gt; – the open-source &lt;a href="https://walkinglabs.github.io/learn-harness-engineering/" rel="noopener noreferrer"&gt;Learn Harness Engineering&lt;/a&gt; course is a solid, hands-on starting point for the fundamentals.&lt;/p&gt;

&lt;p&gt;Thank you for reading 🙏 this blog post was written by &lt;a href="https://alex.thalhammer.name" rel="noopener noreferrer"&gt;Alexander Thalhammer&lt;/a&gt;. For feedback, remarks or questions, please reach out to me ❤️&lt;/p&gt;

</description>
      <category>ai</category>
      <category>angular</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Anthropic Just Released the First Next-Gen Model to the Public</title>
      <dc:creator>Alexander Thalhammer</dc:creator>
      <pubDate>Sat, 13 Jun 2026 11:08:27 +0000</pubDate>
      <link>https://dev.to/lxt/anthropic-just-released-the-first-next-gen-model-to-the-public-4c3k</link>
      <guid>https://dev.to/lxt/anthropic-just-released-the-first-next-gen-model-to-the-public-4c3k</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;strong&gt;Updated three times, on June 13th, June 22nd, and July 4th – see at the bottom.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Like many others, I misread what Anthropic did with their new model Mythos – now officially Mythos&amp;nbsp;5 – over the past few months, when they didn't release it to the public but only to &lt;a href="https://www.anthropic.com/news/expanding-project-glasswing" rel="noopener noreferrer"&gt;Project Glasswing&lt;/a&gt; partners. I assumed it was a marketing stunt: the model would surely be better than the previous ones, but the whole story around it felt exaggerated. Their argument about implementing safeguards and guardrails first – so users cannot abuse the model for malicious exploits against other entities – sounded like an excuse to me. The real reason, I thought, was that they simply didn't have enough compute available, because the new model needs many more tokens and a huge amount of compute.&lt;/p&gt;

&lt;p&gt;I was &lt;strong&gt;wrong&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The safeguards were &lt;strong&gt;not an excuse&lt;/strong&gt;: through Project Glasswing, around 200 vetted organizations across more than 15 countries have been scanning critical codebases – power, water, healthcare, communications – with the Claude Mythos Preview and have found more than 10,000 high- or critical-severity security flaws. And compute was not the real bottleneck either. The public version simply needed new safety classifiers first – for cybersecurity, biology and chemistry, and distillation attempts – which now route affected queries to Opus&amp;nbsp;4.8 in flagged sessions.&lt;/p&gt;

&lt;p&gt;I started &lt;a href="https://www.angulararchitects.io/blog/best-llms-for-angular/" rel="noopener noreferrer"&gt;this blog series&lt;/a&gt; with my AI coding journey, and that journey began in early December – exactly half a year ago, a few weeks after Opus&amp;nbsp;4.5 was released. That release was clearly an inflection point, at least in my personal history, because with that model I could write – or let's say prompt – code for the first time. I hope this doesn't sound arrogant, but I'm quite opinionated about how code should look, and Opus&amp;nbsp;4.5 was the first model that wrote code as if I had written it myself: the same quality, the same style, the same patterns, and so on. Until Opus&amp;nbsp;4.5, I thought these agentic tools just create slop. I was proven &lt;strong&gt;wrong&lt;/strong&gt; six months ago.&lt;/p&gt;

&lt;p&gt;Now I thought the Mythos story was a marketing stunt – and again, I was proven &lt;strong&gt;wrong&lt;/strong&gt;. On June 9, 2026, Anthropic released &lt;a href="https://www.anthropic.com/news/claude-fable-5-mythos-5" rel="noopener noreferrer"&gt;Fable&amp;nbsp;5&lt;/a&gt; to the public – the same underlying model as Mythos&amp;nbsp;5, but with the new safeguards in place. And this isn't just another release. This is a &lt;strong&gt;new generation of LLMs&lt;/strong&gt;, and it is really, really capable.&lt;/p&gt;

&lt;p&gt;You can use it for everything you previously used GPT&amp;nbsp;5.5 or Opus&amp;nbsp;4.8 for – it just does the job even better, with a level of quality and robustness I haven't seen before. I personally have to admit: it writes code better than I would. That is of course not to say I'm obsolete. The model still needs a skilled and senior web developer to steer it in the right direction for a high-quality result. And that is always my goal: to ship high-quality code, no matter what the project or the setup might be.&lt;/p&gt;

&lt;p&gt;Honestly, I didn't plan to publish any model updates in this series, at least not in the first three or even six months, because I'm kind of annoyed by all the model hype posts and videos. But as I already said, I think this model really is a new generation, so I had to cover it here. Yesterday, Manfred asked me why I think this model is a new generation, and it was surprisingly hard for me to answer. I said: "Let me think about it. I will tell you tomorrow – or even better: I will write a blog post about it."&lt;/p&gt;

&lt;p&gt;So this is the post.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Makes Fable&amp;nbsp;5 a New Generation?
&lt;/h2&gt;

&lt;p&gt;The first thing that makes Fable&amp;nbsp;5 a new generation is the quality of the code it writes. The code is not just good, it's really high quality. It follows best practices, it has good structure, it's clean, and it looks like it was written by a &lt;strong&gt;good&lt;/strong&gt; senior developer. This is a huge step up from previous models. I used it to review work I had previously done with GPT&amp;nbsp;5.5 and Opus&amp;nbsp;4.8 – it immediately tracked down bugs, issues, and potential issues. I also used it for simplification, and it handled that just as well. So it can basically be used for everything.&lt;/p&gt;

&lt;p&gt;An incomplete list of outstanding features and capabilities of Fable&amp;nbsp;5 includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Long-horizon autonomy&lt;/strong&gt;: Anthropic says Fable&amp;nbsp;5 &lt;a href="https://www.anthropic.com/news/claude-fable-5-mythos-5" rel="noopener noreferrer"&gt;can stay with a problem far longer than any model before it&lt;/a&gt; and operate for days without intervention. &lt;a href="https://x.com/karpathy/status/2064409694761054332" rel="noopener noreferrer"&gt;Andrej Karpathy called it&lt;/a&gt; a step change of the same order as Opus&amp;nbsp;4.5 was in November – especially for long problem-solving sessions on very difficult problems. I totally agree with him, and that is the actual reason for writing this post. This is the model you point at a problem, not at a file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vague goals in, validated results out&lt;/strong&gt;: you can hand it an instruction as open as "look into options to make this more performant", and it will synthesize ideas, test them – even write its own fuzzers and test beds – and come back with validated results instead of a guess. Skills and elaborate prompt engineering are not really necessary anymore – although I dare to make a prediction: they will play a huge role again in the future, when token efficiency becomes the highest priority.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Token efficiency despite the price tag&lt;/strong&gt;: at roughly €10 per million input and €50 per million output tokens (&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;official pricing&lt;/a&gt; is in US dollars; converted to euros and rounded up a bit), it costs about double Opus&amp;nbsp;4.8 per token. But it needs far fewer tokens per task, so on &lt;a href="https://deepswe.datacurve.ai/blog" rel="noopener noreferrer"&gt;DeepSWE&lt;/a&gt; – still my favorite coding benchmark – the early numbers put it roughly on par with GPT&amp;nbsp;5.5 while beating every Opus score at lower total cost. Artificial Analysis has already run the new model through their coding-agent version of DeepSWE (see the chart below), and the official DeepSWE leaderboard now shows the same story from the benchmark source itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Benchmark jump&lt;/strong&gt;: on the &lt;a href="https://artificialanalysis.ai/" rel="noopener noreferrer"&gt;Artificial Analysis&lt;/a&gt; intelligence index it is now simply the smartest model ever measured, about five points ahead of GPT&amp;nbsp;5.5 – one of the biggest single jumps in a long time. As always: &lt;strong&gt;useful signals, not final answers&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vision&lt;/strong&gt;: for the first time, Anthropic takes the vision lead from OpenAI. That matters more than it sounds for our work – think screenshot-based UI verification of &lt;em&gt;Angular&lt;/em&gt; components and design work with real taste instead of the same old templates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not just coding&lt;/strong&gt;: Anthropic explicitly positions it for finance, research, economics, and law – and the early reviews agree that even its writing is noticeably less LLM slop than before.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To that last bullet, I would personally add design and writing – basically anything from emails to project planning. It is incredibly good at all of these things. It does what you tell it to do, it guides you through the process of whatever you are working on, and it comes back with useful proposals for the next step towards your goal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://artificialanalysis.ai/agents/coding-agents?agents=claude-code-fable-5-max%2Cclaude-code-opus-4-8-max%2Cclaude-code-opus-4-8-medium%2Ccodex-gpt-5-5-medium%2Ccodex-gpt-5-5-xhigh%2Ccursor-cli-composer-2-5-fast%2Cgemini-cli-gemini-3-1-pro-high&amp;amp;coding-agents-performance-chart=deep-swe&amp;amp;coding-agents-harness-comparison-chart=harness-deep-swe" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fquim7l19ykz75m9h17h7.png" alt="DeepSWE Benchmark by Artificial Analysis" width="800" height="544"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;DeepSWE scores for the current coding agents, captured on June 12, 2026 – click the chart for the &lt;a href="https://artificialanalysis.ai/agents/coding-agents?agents=claude-code-fable-5-max%2Cclaude-code-opus-4-8-max%2Cclaude-code-opus-4-8-medium%2Ccodex-gpt-5-5-medium%2Ccodex-gpt-5-5-xhigh%2Ccursor-cli-composer-2-5-fast%2Cgemini-cli-gemini-3-1-pro-high&amp;amp;coding-agents-performance-chart=deep-swe&amp;amp;coding-agents-harness-comparison-chart=harness-deep-swe" rel="noopener noreferrer"&gt;live comparison&lt;/a&gt;. Strictly speaking, these are coding-agent results, so the harness matters: Fable&amp;nbsp;5 runs inside Claude Code at max effort here. The nice bonus of this view is that it even includes Composer&amp;nbsp;2.5, which the original DeepSWE leaderboard could not test due to the missing public API.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://deepswe.datacurve.ai/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8qbf07x6parmy0hdbgxr.png" alt="DeepSWE Fable 5 leaderboard" width="800" height="195"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://deepswe.datacurve.ai/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fapp3zo1w2ofov5v6w2uq.png" alt="DeepSWE Fable 5 cost comparison" width="800" height="541"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The official &lt;a href="https://deepswe.datacurve.ai/" rel="noopener noreferrer"&gt;DeepSWE leaderboard&lt;/a&gt;, updated June 20, 2026, now puts Fable&amp;nbsp;5 at 70% pass@1 on v1.1, narrowly ahead of GPT&amp;nbsp;5.5 at 67% and clearly ahead of Opus&amp;nbsp;4.8 at 59%. The cost view is the important caveat: Fable&amp;nbsp;5 is the strongest model in this slice, but not the cheapest one, so I would use it for hard, long-horizon engineering work where the extra quality actually changes the outcome.&lt;/p&gt;

&lt;p&gt;So this model really is insanely good – almost too good to be true. Which brings us to the obvious question: &lt;strong&gt;what's the catch?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Every Fable Has Its Fine Print
&lt;/h2&gt;

&lt;p&gt;There is not one catch, there are several – some are merely annoying, some might be deal-breakers for your setup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The safeguards are real, and you will notice them.&lt;/strong&gt; Anthropic says fewer than 5% of sessions get routed to Opus&amp;nbsp;4.8, and in my coding work so far that matches – I have not hit a single refusal yet. But Artificial Analysis recorded fallback routing in 8% of their benchmark tasks, mostly on scientific questions. At least the routing is transparent: when it happens, you are told.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One safeguard is silent, though.&lt;/strong&gt; When Fable&amp;nbsp;5 detects that you are working on frontier LLM development, it does not notify you – it quietly limits its own capabilities through prompt modification, steering vectors, and parameter-efficient fine-tuning. Anthropic estimates this affects 0.03% of traffic and fewer than 0.1% of organizations, so most &lt;em&gt;Angular&lt;/em&gt; developers will never encounter it. Still, paying full price for a quietly limited model is a precedent I really don't like.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;There is no zero data retention.&lt;/strong&gt; All Fable&amp;nbsp;5 traffic requires 30-day retention – even for enterprise customers, with no opt-out. Anthropic claims the data is not used for training or any non-safety purposes, but as I wrote in the &lt;a href="https://www.angulararchitects.io/blog/ai-data-privacy-for-angular/" rel="noopener noreferrer"&gt;data privacy post&lt;/a&gt;: "not used for training" does not mean "not retained". For regulated industries and strict company policies, this alone can be a &lt;strong&gt;non-starter&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And now to the three catches that affect all of us directly:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The clock was ticking – faster than expected.&lt;/strong&gt; Fable&amp;nbsp;5 was effectively available to me only from June 10 through &lt;strong&gt;June 12, 2026&lt;/strong&gt;. Originally, I wrote that the included window would close on June 22 and then require usage credits. That turned out to be far too optimistic: on June 12, Anthropic had to disable Fable&amp;nbsp;5 and Mythos&amp;nbsp;5 for all customers after a &lt;a href="https://www.anthropic.com/news/fable-mythos-access" rel="noopener noreferrer"&gt;US government directive&lt;/a&gt;. For roughly three days, we saw the next generation, and then it was gone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It is slow.&lt;/strong&gt; Even simple tasks take one to three minutes, and I have barely seen a run finish faster. This is not a model for the tight interactive loop where Composer&amp;nbsp;2.5 or GPT&amp;nbsp;5.5 on low reasoning shine. But as I argued in the &lt;a href="https://www.angulararchitects.io/blog/ai-costs-for-angular/" rel="noopener noreferrer"&gt;costs post&lt;/a&gt;, the practical speed metric is not tokens per second – it is the end-to-end time until I have a reviewed diff. For long autonomous runs, Fable&amp;nbsp;5's pace is acceptable. For quick edits, use something else.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It is expensive.&lt;/strong&gt; Roughly €10 input and €50 output per million tokens is about double the Opus&amp;nbsp;4.8 price, and the usage limits drain accordingly: early adopters report burning through roughly €100 of usage-based inference in &lt;strong&gt;less than ten minutes&lt;/strong&gt;, and maxing out the 5-hour session limits of two €200 subscriptions in a single evening. Yes, it needs fewer tokens per task, so the &lt;strong&gt;cost per accepted, reviewed, merged change&lt;/strong&gt; can still come out ahead for hard problems. But for everyday edits, this model is simply overkill – and after June 12, it was not priced like anything anymore because it was gone.&lt;/p&gt;

&lt;p&gt;So my original call to action aged badly: we did not have until June 22. We had June 10 to June 12, and then the model was shut down. If you tested it in that tiny window, you got a glimpse of what a next-gen model can do for a real codebase. If not, we are all waiting to see whether Anthropic can bring it back.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Updated Current Setup
&lt;/h2&gt;

&lt;p&gt;In my &lt;a href="https://www.angulararchitects.io/blog/ai-personal-verdict/" rel="noopener noreferrer"&gt;personal verdict&lt;/a&gt;, published less than two weeks ago (in early June 2026), my default was Codex with GPT&amp;nbsp;5.5, complemented by the Claude Desktop app with Opus&amp;nbsp;4.8 for architecture, design, refactoring, and reviews. Three days of Fable&amp;nbsp;5 have turned that ratio upside down:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxeqigfg97ahpgqvbsjex.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxeqigfg97ahpgqvbsjex.png" alt="AI Updated Usage of Claude Fable 5 vs Codex GPT 5.5" width="799" height="543"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Before June 10&lt;/strong&gt;: Codex with GPT&amp;nbsp;5.5 was my default (70%), while the Claude Desktop app with Opus&amp;nbsp;4.8 handled architecture, design, refactoring, and reviews (35%).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;From June 10 to June 12&lt;/strong&gt;: the Claude Desktop app with Fable&amp;nbsp;5 took 80% of my work, Codex with GPT&amp;nbsp;5.5 the remaining 20%.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Attentive readers of that verdict will remember why my old percentages added up to more than 100%: I used to review what each of the two main apps produced with the other challenger. That habit has basically stopped now – there is simply no need to review Fable&amp;nbsp;5's work with GPT&amp;nbsp;5.5, because Fable&amp;nbsp;5 is just way better.&lt;/p&gt;

&lt;p&gt;I do keep using Codex for computer use, image generation, and simple tasks that should be done very fast – exactly the kind of work where Fable&amp;nbsp;5 would be wasted. And I'm not planning to cancel my OpenAI subscription either, because I guess we will soon see &lt;strong&gt;GPT&amp;nbsp;6 as the response to Fable&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So the &lt;a href="https://www.angulararchitects.io/blog/ai-personal-verdict/" rel="noopener noreferrer"&gt;decision tree&lt;/a&gt; from the personal verdict still stands – but its top branch has a new label, at least for now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Human in the Loop – for How Much Longer?
&lt;/h2&gt;

&lt;p&gt;There are basically two camps in the agentic engineering field right now.&lt;/p&gt;

&lt;p&gt;The first camp is the progressive one. Their position: "Just &lt;strong&gt;trust the coding model&lt;/strong&gt;. You don't have to review its work anymore – human review is the new bottleneck." A lot of my friends sit in this camp.&lt;/p&gt;

&lt;p&gt;The second camp says: "You still need to &lt;strong&gt;review everything&lt;/strong&gt; the model builds, because that is the only way to keep slop out of your codebase." If you have read this series, you know which camp I belong to: in the &lt;a href="https://www.angulararchitects.io/blog/ai-apps-harnesses-for-angular/" rel="noopener noreferrer"&gt;apps and harnesses post&lt;/a&gt;, I wrote that I review every diff and expect every generated line to look as if I had handcrafted it myself, and the later &lt;a href="https://www.angulararchitects.io/blog/ae-harness-setup-for-angular/" rel="noopener noreferrer"&gt;harness setup post&lt;/a&gt; turns that idea into concrete guardrails. A lot of my colleagues sit in this camp. Some of them are still trying to find time to even try agentic engineering. Crazy, right?&lt;/p&gt;

&lt;p&gt;I was fully convinced of the human-in-the-loop (HITL) position. The last two days have made me &lt;strong&gt;a lot less convinced&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Not because my standards dropped, but because the reviews changed sides: as described above, I let Fable&amp;nbsp;5 review work that GPT&amp;nbsp;5.5, Opus&amp;nbsp;4.8, and I had produced together – and it found the bugs, the issues, and the simplifications, not me. When the model reviews my work more reliably than I review its work, the question is no longer whether human review is sacred. The question is what it still catches.&lt;/p&gt;

&lt;p&gt;So here is the uncomfortable sentence I did not expect to write this soon: I can imagine that &lt;strong&gt;human reviews really become obsolete&lt;/strong&gt; in the near future – not because we get lazy, but because they stop finding anything the model has not already found itself.&lt;/p&gt;

&lt;p&gt;For now, I have not changed my workflow: every diff still gets my review, and that is also what I still recommend to every professional &lt;em&gt;Angular&lt;/em&gt; team. But for the first time, this feels less like a timeless principle and more like a practice with an expiry date. Ask me again in six months. Or even three. Let's see. Exciting times!&lt;/p&gt;

&lt;h2&gt;
  
  
  Agentic Engineering Workshop
&lt;/h2&gt;

&lt;p&gt;And this is exactly why a new model – even a new generation – does not change the fundamentals: the model, the app, my &lt;em&gt;Angular&lt;/em&gt; Guardrails, my &lt;em&gt;Angular&lt;/em&gt; Coding Style Guide, and the review workflow still belong together. Fable&amp;nbsp;5 just raised the ceiling of what that system can do.&lt;/p&gt;

&lt;p&gt;If you want to judge new models like Fable&amp;nbsp;5 on your own codebase – and put them to work on real modernization, refactoring, testing, and review tasks – join our &lt;strong&gt;Agentic Engineering Workshop&lt;/strong&gt;, available in English and German. In it, advanced &lt;em&gt;Angular&lt;/em&gt; developers learn how to move from vibe coding to traceable Agentic Engineering workflows: AI-ready project setup, guardrails, spec-first and plan-first workflows, UX and component prototyping, code review, testing, and brownfield refactoring.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🤖 &lt;a href="https://www.angulararchitects.io/en/training/agentic-engineering-von-vibe-coding-zu-professionellen-ki-gestuetzten-workflows/" rel="noopener noreferrer"&gt;&lt;strong&gt;Agentic Engineering Workshop&lt;/strong&gt;&lt;/a&gt; – 2 days, remote&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Half a year ago, Opus&amp;nbsp;4.5 convinced me that agentic tools can write real code. Fable&amp;nbsp;5 is the first model since then that gives me the same feeling of crossing a threshold – not because of benchmark wins, but because of what it does in my own daily work: it stays with a problem, it validates its own results, and it writes code I like and want to merge.&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://www.angulararchitects.io/blog/best-llms-for-angular/" rel="noopener noreferrer"&gt;first post of this series&lt;/a&gt;, I wrote that benchmark wins are &lt;strong&gt;useful signals, not final answers&lt;/strong&gt;, and that no single release should change your workflow overnight. I stand by that – and yet Fable&amp;nbsp;5 is the first release that made me update my setup within days, and the first that made me question my own &lt;strong&gt;human-in-the-loop&lt;/strong&gt; dogma. Both things can be true: stay skeptical of the hype, and recognize a new generation when it lands in your terminal.&lt;/p&gt;

&lt;p&gt;The catches are real: the safeguards, the data retention, the speed, the price, and above all the June 12 shutdown. So let me correct my original call to action one last time: the included Fable&amp;nbsp;5 window did not close on June 22; it ended on &lt;strong&gt;June 12, 2026&lt;/strong&gt;, when Anthropic disabled access after the US government directive. If it comes back, judge it the same way I recommended for every model before it – in your own codebase, with your own tasks, your own tools, and your own review standards.&lt;/p&gt;

&lt;p&gt;One last confession: this is my first model-release hype post – exactly the kind I claimed to be annoyed by at the top. I hope I don't have to write another one anytime soon. Wait, there will probably be one when &lt;strong&gt;GPT&amp;nbsp;6&lt;/strong&gt; lands – let's see how long it takes OpenAI to respond.&lt;/p&gt;

&lt;p&gt;Thank you for reading 🙏 this blog post was written by &lt;a href="https://alex.thalhammer.name" rel="noopener noreferrer"&gt;Alexander Thalhammer&lt;/a&gt;. For feedback, remarks or questions, please reach out to me ❤️&lt;/p&gt;

&lt;h2&gt;
  
  
  Update June 13th
&lt;/h2&gt;

&lt;p&gt;So this is the real catch: The model was &lt;a href="https://www.anthropic.com/news/fable-mythos-access" rel="noopener noreferrer"&gt;shut down by the US government&lt;/a&gt;. Really sad. What a shame. So the AI war has escalated – let's see what happens next. For now, we're back to doing more of the work ourselves.&lt;/p&gt;

&lt;h2&gt;
  
  
  Update June 22nd
&lt;/h2&gt;

&lt;p&gt;Inserted new charts by DeepSWE from June 20th. Still waiting for the model to be released again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Update July 4th
&lt;/h2&gt;

&lt;p&gt;Fable&amp;nbsp;5 is &lt;strong&gt;back&lt;/strong&gt;. On July 2nd, Anthropic re-enabled access for subscription customers – for now until &lt;strong&gt;July 7th&lt;/strong&gt;. What happens after that date is unknown. And the model still is everything I described above – I love working with it.&lt;/p&gt;

&lt;p&gt;The price catch, however, is also unchanged: the usage limits drain as fast as ever. My workaround: I let Fable&amp;nbsp;5 do the thinking and delegate to cheaper sub-agents for the legwork – Opus&amp;nbsp;4.8 via the Agent tool and GPT&amp;nbsp;5.5 via Codex – running Fable&amp;nbsp;5 itself at effort level medium to high. That stretches the limits considerably, but not enough: I still max out my plan, so I just bought a second $200 subscription. Make of that what you will – for me, it's worth it.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>agents</category>
    </item>
    <item>
      <title>Agentic Verdict: What's the Best Solution for You?</title>
      <dc:creator>Alexander Thalhammer</dc:creator>
      <pubDate>Fri, 05 Jun 2026 22:00:00 +0000</pubDate>
      <link>https://dev.to/lxt/agentic-verdict-whats-the-best-solution-for-you-46f1</link>
      <guid>https://dev.to/lxt/agentic-verdict-whats-the-best-solution-for-you-46f1</guid>
      <description>&lt;h1&gt;
  
  
  Agentic Verdict: What's the Best Solution for You?
&lt;/h1&gt;

&lt;p&gt;In the first four posts of the original series arc, I looked at &lt;a href="https://www.angulararchitects.io/blog/best-llms-for-angular/" rel="noopener noreferrer"&gt;LLMs&lt;/a&gt;, &lt;a href="https://www.angulararchitects.io/blog/ai-apps-harnesses-for-angular/" rel="noopener noreferrer"&gt;apps and harnesses&lt;/a&gt;, &lt;a href="https://www.angulararchitects.io/blog/ai-costs-for-angular/" rel="noopener noreferrer"&gt;costs&lt;/a&gt;, and the &lt;a href="https://www.angulararchitects.io/blog/ai-data-privacy-for-angular/" rel="noopener noreferrer"&gt;data and governance side&lt;/a&gt; of agentic coding. This fifth post brings those threads together.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Update: This was the original final verdict. Since then I added the &lt;a href="https://www.angulararchitects.io/blog/ai-next-gen-model/" rel="noopener noreferrer"&gt;Fable 5 update&lt;/a&gt; and the &lt;a href="https://www.angulararchitects.io/blog/ae-harness-setup-for-angular/" rel="noopener noreferrer"&gt;harness setup post&lt;/a&gt;, which extend the story without changing the basic decision framework.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I want to start with my current rankings and then move to the practical question I get asked most often: which setup should I actually use?&lt;/p&gt;

&lt;h2&gt;
  
  
  This Is Still Not a Benchmark
&lt;/h2&gt;

&lt;p&gt;One last time: this is my personal verdict for professional &lt;em&gt;Angular&lt;/em&gt; work in enterprise projects (TypeScript, HTML, SCSS), not a scientific benchmark. The benchmarks are useful signals, but the final answer still depends on your codebase, your IDE, your budget, your company rules, and your willingness to review AI-generated code properly – which, in my opinion, is still necessary to avoid slop.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR: My Current Verdict
&lt;/h2&gt;

&lt;p&gt;Once again, my favorite benchmark currently showing coding capabilities, &lt;a href="https://deepswe.datacurve.ai/blog" rel="noopener noreferrer"&gt;DeepSWE&lt;/a&gt;, as discussed in the &lt;a href="https://www.angulararchitects.io/blog/best-llms-for-angular/" rel="noopener noreferrer"&gt;LLMs post&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftle2v3hp7qa80z2xlkok.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftle2v3hp7qa80z2xlkok.png" alt="Recent DeepSWE benchmark" width="800" height="615"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And if you're an enterprise and tokens and costs matter to you, this chart from the &lt;a href="https://www.angulararchitects.io/blog/ai-costs-for-angular/" rel="noopener noreferrer"&gt;costs post&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F462tz89t9of0x8qalx56.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F462tz89t9of0x8qalx56.png" alt="AI Cost Comparison Chart" width="799" height="541"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  And the Winner (My Current Setup) Is
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;My default solution (70%)&lt;/strong&gt;: &lt;a href="https://openai.com/codex/" rel="noopener noreferrer"&gt;Codex&lt;/a&gt; app with &lt;a href="https://openai.com/index/introducing-gpt-5-5/" rel="noopener noreferrer"&gt;GPT 5.5&lt;/a&gt; – very good, fast (depending on effort), reliable (€100/month sub).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For architecture, design, refactoring, writing, and often just reviewing Codex/GPT 5.5 work (35%)&lt;/strong&gt;: &lt;a href="https://www.anthropic.com/product/claude-code" rel="noopener noreferrer"&gt;Claude Desktop&lt;/a&gt; with &lt;a href="https://www.anthropic.com/news/claude-opus-4-8" rel="noopener noreferrer"&gt;Opus 4.8&lt;/a&gt;. To me, it's the best model currently for complex, high-quality &lt;em&gt;Angular&lt;/em&gt; work, but it's pretty slow and the app experience is not as good as in Codex (€100/month sub).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For careful handcrafted &lt;em&gt;Angular&lt;/em&gt; work (5%)&lt;/strong&gt;: &lt;a href="https://www.jetbrains.com/webstorm/" rel="noopener noreferrer"&gt;WebStorm&lt;/a&gt; with &lt;a href="https://www.jetbrains.com/help/ai-assistant/junie-agent.html" rel="noopener noreferrer"&gt;Junie&lt;/a&gt; and Opus 4.8 (€20/month sub &amp;amp; API pricing).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For high-speed work (2.5%)&lt;/strong&gt;: &lt;a href="https://cursor.com/" rel="noopener noreferrer"&gt;Cursor&lt;/a&gt; with &lt;a href="https://cursor.com/changelog/composer-2-5" rel="noopener noreferrer"&gt;Composer 2.5&lt;/a&gt; (€20/month sub).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For demoing why Antigravity and Gemini are pretty useless (0.1%)&lt;/strong&gt;: &lt;a href="https://antigravity.google/download" rel="noopener noreferrer"&gt;Antigravity&lt;/a&gt; with &lt;a href="https://blog.google/innovation-and-ai/models-and-research/gemini-models/gemini-3-5/" rel="noopener noreferrer"&gt;Gemini&lt;/a&gt; (€20/month sub, cos I had Drive anyway).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For showing why Copilot is no good (0%)&lt;/strong&gt;: &lt;a href="https://github.com/features/copilot/plans" rel="noopener noreferrer"&gt;GitHub Copilot&lt;/a&gt; with any model (€8/month sub).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In parentheses, the percentage is a rough estimate of how much of my work currently goes through that setup. The percentages add up to more than 100% because I often use multiple setups for the same task – e.g. I might start with Codex/GPT 5.5 for implementation and then switch to Claude/Opus 4.8 for review and refactoring. And sometimes I just want to compare two models on the same task, which is also a good way to keep track of how they evolve.&lt;/p&gt;

&lt;p&gt;Whichever of these you settle on, picking the app is only step one – the bigger lever is how you set up the &lt;strong&gt;harness&lt;/strong&gt; around it. I walk through that end to end for &lt;em&gt;Angular&lt;/em&gt; in the &lt;a href="https://www.angulararchitects.io/blog/ae-harness-setup-for-angular/" rel="noopener noreferrer"&gt;harness setup post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So roughly that sums up to €270 each month (heavy subsidization makes it possible). Considering my hourly rate of €150–300, that means that I have to save 1–2h per &lt;strong&gt;month&lt;/strong&gt;. Mediocre quote of former mayor of Vienna (legendary &lt;a href="https://en.wikipedia.org/wiki/Michael_H%C3%A4upl" rel="noopener noreferrer"&gt;Michael Häupl&lt;/a&gt;) incoming: That's done during lunch on the 1st day of the month. Still considering not using agentic coding? I hope not.&lt;/p&gt;

&lt;p&gt;I do actually hate the fact that in Codex I cannot switch to other model providers, because if I could, I would stop using everything else. I also hate the fact that Google has fallen far behind. Of all the big labs, I actually like Google the most as a company. But they do seem to have an organizational problem and cannot currently compete against the three frontier labs – OpenAI, Anthropic, and now Cursor, which after the &lt;a href="https://cursor.com/blog/spacex-model-training" rel="noopener noreferrer"&gt;SpaceXAI deal&lt;/a&gt; and Composer 2.5 I count as part of the frontier rather than a challenger. Hopefully Google Gemini will catch up. We need competition, and three frontier labs are already better than the old "duopoly".&lt;/p&gt;

&lt;p&gt;And of course I do prefer open source tools like &lt;a href="https://opencode.ai/" rel="noopener noreferrer"&gt;OpenCode&lt;/a&gt; or &lt;a href="https://t3.codes/" rel="noopener noreferrer"&gt;T3 Code&lt;/a&gt; where you can switch the model provider, because that gives you more freedom and flexibility. And I'd love to use open weight models. But for now those are not good enough for my work. This will probably change in 6–12 months, and I'll update my verdict &amp;amp; my recommendations accordingly.&lt;/p&gt;

&lt;p&gt;Also, I worry that Chinese labs may become less generous with open weight releases. That would be understandable after Cursor's recent coup of building their astonishing Composer 2.5 upon &lt;a href="https://cursor.com/blog/composer-2-5" rel="noopener noreferrer"&gt;&lt;strong&gt;Moonshot's Kimi K2.5&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;However, I won't let them (neither OpenAI nor Anthropic) lock me in, so I will keep trying other setups and keep an eye on the market. But for now, this is my setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is the Best Solution for You?
&lt;/h2&gt;

&lt;p&gt;Yes, the best solution for you depends on your company policy, your workflow preferences, and your willingness to review AI-generated code carefully. But choosing your setup is actually not that complicated.&lt;/p&gt;

&lt;p&gt;Nevertheless, I have created a &lt;strong&gt;sophisticated decision tree&lt;/strong&gt; to help ya out (click to enlarge):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.angulararchitects.io/wp-content/uploads/2026/06/ai-verdict-decision-tree.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffw8mx7b9wymtclp02dio.png" alt="Agentic Coding Decision Tree" width="800" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  But Alex, My Favorite IDE Is Not Mentioned
&lt;/h3&gt;

&lt;p&gt;I hear ya. Of course I mention the IDEs that I've been using the most – which is nothing other than personal preference. If you're using something different, try to apply the pattern from the decision tree above:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Does my tool have an agent harness built in? If yes, go with the flow. If no, use Codex &amp;amp; Claude Desktop.&lt;/li&gt;
&lt;li&gt;Does my tool support plugins/extensions for Codex/Claude Code? If yes, check out those plugins. If they're good, use them. If not, fall back to Codex &amp;amp; Claude Desktop.&lt;/li&gt;
&lt;li&gt;If you're using something like Neovim, use Codex CLI &amp;amp; Claude Code CLI. Fine with me.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Do your own research, find the &lt;strong&gt;app &amp;amp; model that suits your preference, workflow, policy and ethics best&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Effort and Fast Mode
&lt;/h2&gt;

&lt;p&gt;Easy: As I explained in the &lt;a href="https://www.angulararchitects.io/blog/ai-costs-for-angular/" rel="noopener noreferrer"&gt;costs post&lt;/a&gt;, start with &lt;code&gt;high&lt;/code&gt; effort. If your results are perfect, reduce effort to &lt;code&gt;medium&lt;/code&gt; or &lt;code&gt;low&lt;/code&gt;. If there are problems, try &lt;code&gt;extra&lt;/code&gt; (or even &lt;code&gt;max&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Enable &lt;code&gt;fast&lt;/code&gt; mode by default. For the tradeoff, see the &lt;a href="https://developers.openai.com/codex/pricing/" rel="noopener noreferrer"&gt;Codex speed configuration pricing&lt;/a&gt; and the &lt;a href="https://platform.claude.com/docs/en/build-with-claude/fast-mode" rel="noopener noreferrer"&gt;Claude fast mode docs&lt;/a&gt;. If you hit your usage limits, turn it off or upgrade.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Plan Do I Choose?
&lt;/h2&gt;

&lt;p&gt;Also, an easy choice: start with the €20 plans. Upgrade once you see return on investment and frequently run into limits.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Would Recommend to Teams
&lt;/h2&gt;

&lt;p&gt;For teams, I would recommend trying the &lt;a href="https://www.angulararchitects.io/blog/ai-costs-for-angular/" rel="noopener noreferrer"&gt;Team Plan&lt;/a&gt; if your procurement and &lt;a href="https://www.angulararchitects.io/blog/ai-data-privacy-for-angular/" rel="noopener noreferrer"&gt;company policy&lt;/a&gt; allow it. Additionally:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pick one or two approved tools instead of banning everything.&lt;/li&gt;
&lt;li&gt;Start with a pilot on real &lt;em&gt;Angular&lt;/em&gt; work.&lt;/li&gt;
&lt;li&gt;Measure accepted changes, review quality, and cost.&lt;/li&gt;
&lt;li&gt;Keep humans responsible for the final diff.&lt;/li&gt;
&lt;li&gt;Review the setup again every few months because the tools change too quickly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Enterprise / API Pricing Considerations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Cursor &amp;amp; Composer 2.5
&lt;/h3&gt;

&lt;p&gt;If you can (depending on your setup, company policy, and personal ethics), I would recommend trying Cursor with Composer 2.5 for very good API pricing. It's the cheapest and the fastest. Results are somewhere between the frontier models (GPT 5.5 and Opus 4.8). So for ROI this might be your best bet – at least currently.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzox6xiuiue1k1ivpqrol.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzox6xiuiue1k1ivpqrol.png" alt="LLM API Model Pricing" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Codex, GPT (and Claude Opus) With Low Effort
&lt;/h3&gt;

&lt;p&gt;However, if you want to use the models with the best coding capabilities, I would recommend using Codex with GPT 5.5 (and Claude with Opus 4.8) with low to medium effort.&lt;/p&gt;

&lt;p&gt;There is no need to go back in model history or use the Sonnet models. By reducing the effort you can easily reduce the costs dramatically, while still getting good results.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agentic Engineering Workshop
&lt;/h2&gt;

&lt;p&gt;If this verdict has shown one thing, it is that none of these decisions stands alone: model, harness, costs, privacy, and workflow are one setup – choose them one by one and you end up with an expensive mess.&lt;/p&gt;

&lt;p&gt;If you want to bring all of that into one coherent, professional &lt;em&gt;Angular&lt;/em&gt; setup – join our &lt;strong&gt;Agentic Engineering Workshop&lt;/strong&gt;, available in English and German. Together we take advanced &lt;em&gt;Angular&lt;/em&gt; developers from vibe coding to traceable Agentic Engineering workflows: AI-ready project setup, guardrails, spec-first and plan-first workflows, UX and component prototyping, code review, testing, and brownfield refactoring.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🤖 &lt;a href="https://www.angulararchitects.io/en/training/agentic-engineering-von-vibe-coding-zu-professionellen-ki-gestuetzten-workflows/" rel="noopener noreferrer"&gt;&lt;strong&gt;Agentic Engineering Workshop&lt;/strong&gt;&lt;/a&gt; – 2 days, remote&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This series started with a single question – &lt;a href="https://www.angulararchitects.io/blog/best-llms-for-angular/" rel="noopener noreferrer"&gt;which LLM is best for &lt;em&gt;Angular&lt;/em&gt;&lt;/a&gt; – and slowly turned into something bigger, because no part of this works in isolation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The model matters.&lt;/strong&gt; &lt;a href="https://www.angulararchitects.io/blog/best-llms-for-angular/" rel="noopener noreferrer"&gt;Opus 4.8 and GPT 5.5&lt;/a&gt; are genuinely different tools, and knowing when to reach for which one is half the battle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The harness matters.&lt;/strong&gt; &lt;a href="https://www.angulararchitects.io/blog/ai-apps-harnesses-for-angular/" rel="noopener noreferrer"&gt;Codex, the Claude Desktop app, Cursor, and your custom setup&lt;/a&gt; each shape the workflow differently – and &lt;a href="https://www.angulararchitects.io/blog/ae-harness-setup-for-angular/" rel="noopener noreferrer"&gt;setting that harness up properly for &lt;em&gt;Angular&lt;/em&gt;&lt;/a&gt; is the highest-leverage thing you can do.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The pricing model matters.&lt;/strong&gt; What counts is not the price per token but the &lt;a href="https://www.angulararchitects.io/blog/ai-costs-for-angular/" rel="noopener noreferrer"&gt;&lt;strong&gt;cost per accepted, reviewed, merged change&lt;/strong&gt;&lt;/a&gt; – and a subsidized subscription is still the best deal you can currently get.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And the data side matters too.&lt;/strong&gt; &lt;a href="https://www.angulararchitects.io/blog/ai-data-privacy-for-angular/" rel="noopener noreferrer"&gt;Don't only say no&lt;/a&gt;. Approve a few safe paths and make the official workflow better than the unofficial workaround.&lt;/p&gt;

&lt;p&gt;But if there is one thing to take away from the whole arc, it is this: the best setup is &lt;strong&gt;not the one that wins benchmarks, it is the one that fits your actual development workflow and still keeps code quality high&lt;/strong&gt;. Pick a setup, run it on real &lt;em&gt;Angular&lt;/em&gt; work, measure what it changes, and revisit it in a few months – because by then half of this will have moved again.&lt;/p&gt;

&lt;p&gt;The teams that learn to do this professionally won't just write code faster. They will modernize faster, test faster, review faster, and learn faster. And they will stay competitive. That is the whole point.&lt;/p&gt;

&lt;p&gt;Thank you for reading 🙏 this blog post was written by &lt;a href="https://alex.thalhammer.name" rel="noopener noreferrer"&gt;Alexander Thalhammer&lt;/a&gt;. For feedback, remarks or questions, please reach out to me ❤️&lt;/p&gt;

&lt;h2&gt;
  
  
  Update June 21st
&lt;/h2&gt;

&lt;p&gt;Updated with the new charts v1.1 by DeepSWE .&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>agents</category>
    </item>
    <item>
      <title>Agentic Engineering: What Do AI Coding Tools Do With Your Code?</title>
      <dc:creator>Alexander Thalhammer</dc:creator>
      <pubDate>Fri, 05 Jun 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/lxt/agentic-engineering-what-do-ai-coding-tools-do-with-your-code-2mjn</link>
      <guid>https://dev.to/lxt/agentic-engineering-what-do-ai-coding-tools-do-with-your-code-2mjn</guid>
      <description>&lt;p&gt;In the &lt;a href="https://www.angulararchitects.io/blog/best-llms-for-angular/" rel="noopener noreferrer"&gt;first post&lt;/a&gt; of this series arc, I wrote about the LLMs I currently like to use for &lt;em&gt;Angular&lt;/em&gt; development. In the &lt;a href="https://www.angulararchitects.io/blog/ai-apps-harnesses-for-angular/" rel="noopener noreferrer"&gt;second post&lt;/a&gt;, I looked at the apps and harnesses around those models: Codex, Claude Desktop app, Cursor, Antigravity, VS Code, WebStorm, and a few more. In the &lt;a href="https://www.angulararchitects.io/blog/ai-costs-for-angular/" rel="noopener noreferrer"&gt;third post&lt;/a&gt;, I focused on money: subscriptions, API costs, enterprise plans, token usage, reasoning levels, and cost control. I originally planned to squeeze all of this into that costs post, but it quickly became too much for one article, so both topics now get their own. This fourth part is about the less comfortable question behind all of that:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What do we actually share when we use agentic coding tools?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is where pricing, privacy, enterprise rules, EU regulations, and the very common sentence "We are not allowed to use AI here" all meet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is our code safe?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before I dive in: this is not legal advice and no replacement for your company's security review – just my practical view as an &lt;em&gt;Angular&lt;/em&gt; builder and coach who refuses to pretend that data protection is somebody else's problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR: Do Not Ban AI, Approve Safe Paths
&lt;/h2&gt;

&lt;p&gt;If you only have thirty seconds, my practical view is this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;personal subscriptions are great for learning, but not automatically fine for proprietary code&lt;/li&gt;
&lt;li&gt;"not used for training" does not mean "not retained"&lt;/li&gt;
&lt;li&gt;secrets, production data, customer data, and regulated personal data need clear rules&lt;/li&gt;
&lt;li&gt;agents don't just read code, they run commands and can be tricked into leaking it, so limit them&lt;/li&gt;
&lt;li&gt;companies should approve a few safe tools instead of forcing unofficial workarounds&lt;/li&gt;
&lt;li&gt;every generated &lt;strong&gt;diff still needs human review&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  This Is Not a Legal Guide
&lt;/h2&gt;

&lt;p&gt;You know the drill by now: my current opinion as of June 2026, based on too much time spent reading vendor documentation.&lt;/p&gt;

&lt;p&gt;And these details change fast. Vendors change plans, defaults, retention settings, enterprise controls, and subprocessors, sometimes within weeks. While I was writing this series, three new models got released (Gemini 3.5 Flash, Composer 2.5 and Opus 4.8). The next model will be out next week I guess (we're waiting for GPT 5.6, right?). Google is folding &lt;a href="https://developers.googleblog.com/an-important-update-transitioning-gemini-cli-to-antigravity-cli/" rel="noopener noreferrer"&gt;Gemini CLI and Gemini Code Assist for individual users into Antigravity CLI&lt;/a&gt;, while enterprise access remains unchanged. So please check the official pages again before you decide anything for real, and read this as a practical field report.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Do We Actually Share?
&lt;/h2&gt;

&lt;p&gt;This was the part where I had to do the most research because it is easy to have a strong opinion here without knowing the details. When you use an AI coding agent, you might share much more than the prompt in the chat box.&lt;/p&gt;

&lt;p&gt;Depending on the tool and settings, the provider or tool vendor may receive:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;your prompt&lt;/li&gt;
&lt;li&gt;selected code snippets&lt;/li&gt;
&lt;li&gt;open and neighboring files&lt;/li&gt;
&lt;li&gt;search results from your repository&lt;/li&gt;
&lt;li&gt;diffs&lt;/li&gt;
&lt;li&gt;terminal commands and outputs&lt;/li&gt;
&lt;li&gt;stack traces&lt;/li&gt;
&lt;li&gt;package names and dependency versions&lt;/li&gt;
&lt;li&gt;file paths&lt;/li&gt;
&lt;li&gt;screenshots&lt;/li&gt;
&lt;li&gt;browser state&lt;/li&gt;
&lt;li&gt;issue, pull request, and review text&lt;/li&gt;
&lt;li&gt;telemetry about accepted suggestions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This does not automatically mean that the provider trains on all of that. Training is a separate question. But AI coding is still a real data-processing workflow.&lt;/p&gt;

&lt;p&gt;For an &lt;em&gt;Angular&lt;/em&gt; project, this can include product logic, internal architecture, API names, feature flags, business rules, test fixtures, local logs, screenshots, and sometimes personal data.&lt;/p&gt;

&lt;p&gt;So the basic rule is simple:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not send secrets, production data, customer data, or regulated personal data to AI tools unless your legal and technical setup explicitly allows it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Agents can see terminal output and files. If your &lt;code&gt;.env&lt;/code&gt; files, logs, database dumps, screenshots, or test fixtures contain sensitive data, the agent might see them too – researchers have shown that Codex, Claude Code, and Cursor &lt;a href="https://codex.danielvaughan.com/2026/05/10/codex-cli-secrets-defence-env-leakage-agent-vault-runtime-injection/" rel="noopener noreferrer"&gt;walk the working directory and can read &lt;code&gt;.env&lt;/code&gt; files&lt;/a&gt; that &lt;code&gt;.gitignore&lt;/code&gt; would normally keep out of sight.&lt;/p&gt;

&lt;h2&gt;
  
  
  More Than Reading: Breach and Industrial Espionage
&lt;/h2&gt;

&lt;p&gt;Here is the part that worries me more than training. These tools are not passive chat boxes. They &lt;strong&gt;run commands&lt;/strong&gt;, hit the &lt;strong&gt;network&lt;/strong&gt;, and call &lt;strong&gt;MCP servers&lt;/strong&gt;, plugins, and connectors. Codex documents an &lt;a href="https://developers.openai.com/codex/concepts/sandboxing" rel="noopener noreferrer"&gt;OS-level sandbox&lt;/a&gt; with &lt;a href="https://developers.openai.com/codex/agent-approvals-security" rel="noopener noreferrer"&gt;approval modes&lt;/a&gt;, and Claude Code ships &lt;a href="https://www.anthropic.com/engineering/claude-code-sandboxing" rel="noopener noreferrer"&gt;sandboxing&lt;/a&gt; built on &lt;code&gt;bubblewrap&lt;/code&gt; and &lt;code&gt;seatbelt&lt;/code&gt;. Good engineering – but a sandbox is a fence, not a law of physics.&lt;/p&gt;

&lt;p&gt;Simon Willison calls the dangerous mix the &lt;a href="https://simonwillison.net/2025/Jun/16/the-lethal-trifecta/" rel="noopener noreferrer"&gt;&lt;strong&gt;"lethal trifecta"&lt;/strong&gt;&lt;/a&gt;: access to private data, exposure to untrusted content, and a way to send data back out. A coding agent in your repo has all three by default. And the fences leak: one team &lt;a href="https://ona.com/stories/how-claude-code-escapes-its-own-denylist-and-sandbox" rel="noopener noreferrer"&gt;showed Claude Code escaping its own denylist&lt;/a&gt; and disabling its sandbox, while researchers have documented &lt;a href="https://arxiv.org/abs/2601.17549" rel="noopener noreferrer"&gt;protocol-level holes in MCP&lt;/a&gt; that enable &lt;a href="https://www.practical-devsecops.com/mcp-security-vulnerabilities/" rel="noopener noreferrer"&gt;prompt injection and tool poisoning&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So let me say the quiet part out loud. For an enterprise &lt;em&gt;Angular&lt;/em&gt; project, the real fear is not that the model learned some generic pattern. It is &lt;strong&gt;data breach and industrial espionage&lt;/strong&gt;: your architecture, business rules, unreleased features, and customer data ending up where they should not.&lt;/p&gt;

&lt;p&gt;It happens to the labs too. In April 2026, Anthropic &lt;a href="https://www.theregister.com/2026/04/01/claude_code_source_leak_privacy_nightmare/" rel="noopener noreferrer"&gt;accidentally exposed Claude Code's own source code&lt;/a&gt; through a source-map file in an npm package – a &lt;a href="https://www.netzwoche.ch/news/2026-04-02/anthropic-veroeffentlicht-versehentlich-quellcode-seines-ki-entwicklertools" rel="noopener noreferrer"&gt;human configuration error&lt;/a&gt;, not an attack, and no customer data was involved. But the lesson is uncomfortable: if the company that builds the agent can leak code by accident, your proprietary code is also just data on someone else's servers, under their rules. Add a prompt-injection vector – a poisoned dependency, a malicious issue, a booby-trapped page the agent reads – and the same agent that sees your code can be &lt;strong&gt;tricked into sending it out&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The takeaway is not panic. It is &lt;strong&gt;threat modelling&lt;/strong&gt;: where does my code live, who can reach it, how long is it kept, and what can the agent be talked into doing? That is why constraining the agent's hands – command execution, network, MCP, connectors – matters as much as the training checkbox.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Found in Vendor Docs
&lt;/h2&gt;

&lt;p&gt;The exact wording differs between vendors, plans, products, and regions, so I would not treat the links below as a permanent legal answer. But they show why teams have to distinguish between training, retention, product telemetry, enterprise terms, and the actual tool context an agent can see.&lt;/p&gt;

&lt;h3&gt;
  
  
  Consumer Plans vs Business Plans
&lt;/h3&gt;

&lt;p&gt;This is the most important distinction I found. Consumer plans often have data controls, but you need to check and configure them. Business, enterprise, and API products usually have stronger defaults, especially around model training.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OpenAI's&lt;/strong&gt; help page on &lt;a href="https://help.openai.com/en/articles/5722486-how-your-data-is-used-to-improve-model-performance" rel="noopener noreferrer"&gt;how data is used to improve model performance&lt;/a&gt; says individual services such as ChatGPT and Codex may use content to train models unless you opt out, but that it does not train on business products, ChatGPT Enterprise, and the API by default. The &lt;a href="https://help.openai.com/en/articles/11369540-codex-and-chatgpt-plan-usage-limits" rel="noopener noreferrer"&gt;Codex plan usage article&lt;/a&gt; says the same for Codex.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Anthropic's&lt;/strong&gt; &lt;a href="https://code.claude.com/docs/en/data-usage" rel="noopener noreferrer"&gt;Claude Code data usage page&lt;/a&gt; makes a similar distinction. Free, Pro, and Max users can allow data use for model improvement; for Team, Enterprise, API, third-party platforms, and Claude Gov, Anthropic says it does not train generative models on Claude Code prompts or code under commercial terms unless the customer opts in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cursor's&lt;/strong&gt; &lt;a href="https://cursor.com/pricing" rel="noopener noreferrer"&gt;pricing page&lt;/a&gt; says Privacy Mode, when enabled in settings or by a team admin, keeps code data from being stored by model providers or used for training. The &lt;a href="https://cursor.com/security" rel="noopener noreferrer"&gt;security page&lt;/a&gt; adds that it is available to anyone, enabled by default for team members, and backed by technical controls and contractual terms such as zero data retention with model providers; the &lt;a href="https://cursor.com/data-use" rel="noopener noreferrer"&gt;data-use overview&lt;/a&gt; spells out what is and isn't stored.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Google's&lt;/strong&gt; consumer Gemini story also differs from the Google Cloud one. The &lt;a href="https://support.google.com/gemini/answer/13594961?hl=en" rel="noopener noreferrer"&gt;Gemini Apps Privacy Hub&lt;/a&gt; warns users not to enter confidential information they would not want reviewed or used to improve services. In contrast, the &lt;a href="https://docs.cloud.google.com/gemini/docs/discover/data-governance" rel="noopener noreferrer"&gt;Gemini for Google Cloud data governance docs&lt;/a&gt; say it does not use prompts or responses to train models, and the &lt;a href="https://docs.cloud.google.com/gemini/docs/codeassist/security-privacy-compliance" rel="noopener noreferrer"&gt;Gemini Code Assist Standard/Enterprise security docs&lt;/a&gt; describe coding context as Customer Data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub Copilot&lt;/strong&gt; just made this split concrete. In March 2026, GitHub changed its defaults so interaction data from Free, Pro, and Pro+ users can train its models unless you opt out, while Business and Enterprise stay excluded. &lt;a href="https://www.helpnetsecurity.com/2026/03/26/github-copilot-data-privacy-policy-update/" rel="noopener noreferrer"&gt;Independent reporting&lt;/a&gt; spelled out what that means for regulated industries, and the &lt;a href="https://copilot.github.trust.page/" rel="noopener noreferrer"&gt;Copilot Trust Center&lt;/a&gt; holds the current details. Same lesson, fresh example.&lt;/p&gt;

&lt;p&gt;So my practical conclusion is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A personal €20 subscription is great for learning and experimentation, but it is not automatically the right setup for proprietary enterprise code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Maybe it is allowed in your company after opt-out and review. Maybe it is not. But you should not guess.&lt;/p&gt;

&lt;h3&gt;
  
  
  No Training Is Not the Same as No Retention
&lt;/h3&gt;

&lt;p&gt;Another thing that is easy to mix up: &lt;strong&gt;"not used for training" does not mean "not retained"&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Providers may still retain data for abuse monitoring, security, debugging, billing, legal obligations, or product functionality. Some enterprise plans allow custom retention, some API setups offer &lt;a href="https://openai.com/enterprise-privacy/" rel="noopener noreferrer"&gt;zero data retention&lt;/a&gt;, and some consumer products keep conversation history unless you delete it or use temporary modes.&lt;/p&gt;

&lt;p&gt;For example, Anthropic's &lt;a href="https://privacy.claude.com/en/articles/10023548-how-long-do-you-store-my-data" rel="noopener noreferrer"&gt;privacy center&lt;/a&gt; says consumer users who do not allow data use for model improvement have 30-day retention, while those who do have 5-year retention.&lt;/p&gt;

&lt;p&gt;OpenAI says individual users can opt out of training, and Temporary Chat will not appear in history, create memories, or be used to train models, though feedback may still be used.&lt;/p&gt;

&lt;p&gt;Google Cloud says Gemini Code Assist Standard and Enterprise are stateless Google Cloud services and do not store prompts and responses in Google Cloud unless you configure logging.&lt;/p&gt;

&lt;p&gt;For a serious company rollout, I would ask every vendor boring but important questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is our code used for training by default?&lt;/li&gt;
&lt;li&gt;Can we opt out organization-wide?&lt;/li&gt;
&lt;li&gt;What is retained, where, and for how long?&lt;/li&gt;
&lt;li&gt;Can we configure retention or use zero data retention?&lt;/li&gt;
&lt;li&gt;Do you offer EU or regional processing?&lt;/li&gt;
&lt;li&gt;Which subprocessors are involved?&lt;/li&gt;
&lt;li&gt;Are human reviewers involved?&lt;/li&gt;
&lt;li&gt;How are prompts, responses, diffs, screenshots, terminal outputs, and telemetry treated?&lt;/li&gt;
&lt;li&gt;Can admins disable connectors, browser access, MCP servers, cloud agents, or external network access?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If a vendor cannot answer these questions clearly, I would not roll it out broadly in a regulated company.&lt;/p&gt;

&lt;h2&gt;
  
  
  EU and Regulatory Considerations
&lt;/h2&gt;

&lt;p&gt;Since I work in Europe, I also care about the EU side. For many companies, GDPR is the main topic; depending on the use case, works councils, vendor management, data residency, contractual confidentiality, and the EU AI Act can also become relevant.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://digital-strategy.ec.europa.eu/en/policies/regulatory-framework-ai" rel="noopener noreferrer"&gt;EU AI Act&lt;/a&gt; entered into force in 2024 and applies gradually. The European Commission's &lt;a href="https://ai-act-service-desk.ec.europa.eu/en/ai-act/timeline/timeline-implementation-eu-ai-act" rel="noopener noreferrer"&gt;implementation timeline&lt;/a&gt; shows obligations for general-purpose AI models started in 2025, and the Commission's enforcement powers start August 2, 2026 – not far away. For a normal &lt;em&gt;Angular&lt;/em&gt; developer using a coding assistant, &lt;strong&gt;GDPR and confidentiality are probably more immediate than the AI Act&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In the DACH region, the practical questions are usually a data processing agreement (Auftragsverarbeitung) and EU data residency – German write-ups like this &lt;a href="https://www.proliance.ai/blog/ki-tools-dsgvo-vergleich-chatgpt-copilot-gemini" rel="noopener noreferrer"&gt;DSGVO comparison of ChatGPT, Copilot, and Gemini&lt;/a&gt; and this &lt;a href="https://compound.law/de-DE/tools/claude-code-dsgvo/" rel="noopener noreferrer"&gt;Claude Code AVV walkthrough&lt;/a&gt; are a decent starting point. But if your company builds AI features into products, uses agents in regulated workflows, or lets AI systems influence decisions, the AI Act becomes much more relevant.&lt;/p&gt;

&lt;p&gt;For coding agents inside a company, I would keep the policy practical:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;classify which repositories may be used with which AI tools&lt;/li&gt;
&lt;li&gt;ban secrets and production data in AI context&lt;/li&gt;
&lt;li&gt;require approved business, team, enterprise, or API terms for proprietary code&lt;/li&gt;
&lt;li&gt;document approved vendors and subprocessors&lt;/li&gt;
&lt;li&gt;require human review for generated code&lt;/li&gt;
&lt;li&gt;educate developers on prompt privacy and tool permissions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal should not be bureaucracy – we have got enough of that.&lt;/p&gt;

&lt;p&gt;The goal should be to &lt;strong&gt;make AI usage possible&lt;/strong&gt; without pushing developers into unofficial workarounds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Green, Yellow, Red
&lt;/h2&gt;

&lt;p&gt;If I had to explain the policy to developers, I would use a very simple traffic-light model.&lt;br&gt;
Not because it covers every legal edge case, but because people actually remember it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2onhlaif5hmv1gb2gwl9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2onhlaif5hmv1gb2gwl9.png" alt="A Simple Traffic-light Rule for Agentic Coding Tools" width="800" height="551"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Green&lt;/strong&gt;: learning, experiments, public and open-source code – personal subscriptions are fine here.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Yellow&lt;/strong&gt;: proprietary code – allowed, but only on approved business, team, enterprise, or API terms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Red&lt;/strong&gt;: secrets, credentials, production data, customer data, regulated personal data – never in the AI context, no exceptions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most real enterprise &lt;em&gt;Angular&lt;/em&gt; work lives in &lt;strong&gt;the yellow zone&lt;/strong&gt;. That is why a blanket ban is too blunt, but "just use whatever" is too naive.&lt;/p&gt;

&lt;h2&gt;
  
  
  "We Are Not Allowed to Use AI"
&lt;/h2&gt;

&lt;p&gt;This is the part where I will be a bit opinionated.&lt;/p&gt;

&lt;p&gt;I hear "we are not allowed to use AI" more and more often. Sometimes that is justified: if a company has no vendor review, no DPA, no data policy, no security story, and developers paste sensitive code into random tools, somebody should stop that. But a blanket "no AI" policy is not a serious long-term strategy. It has a hidden cost: the company becomes slower while competitors learn to use these tools safely. Long-term, I don't think that is survivable.&lt;/p&gt;

&lt;p&gt;I believe professional software teams &lt;strong&gt;need agentic coding to stay competitive&lt;/strong&gt;. Not because AI magically replaces good engineers. It does not. But because the baseline speed of software work is changing.&lt;/p&gt;

&lt;p&gt;If one team can modernize code, write tests, review changes, and explore refactorings faster, the other team cannot keep working exactly as before and expect no consequences.&lt;/p&gt;

&lt;p&gt;For &lt;em&gt;Angular&lt;/em&gt; teams this is especially relevant. Enterprise &lt;em&gt;Angular&lt;/em&gt; projects often have large, complex codebases, old RxJS flows, inconsistent test coverage, slow migrations, and a lot of technical debt – a lot of boring (I personally love it!) but important modernization work. Agentic coding helps with exactly that – if we keep strong human-in-the-loop review and a sophisticated &lt;a href="https://github.com/L-X-T/ng-agentic/blob/main/style-guide/style-guide.md" rel="noopener noreferrer"&gt;&lt;em&gt;Angular&lt;/em&gt; Coding Style Guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So I would not argue for "let everybody use everything". I would argue for this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not ban AI. Approve a small number of safe paths and teach people to use them professionally.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That is the better security posture.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Simple Company Policy
&lt;/h2&gt;

&lt;p&gt;If I had to write a first version of an AI coding policy for an &lt;em&gt;Angular&lt;/em&gt; company, I would keep it short:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use approved tools only (might have to approve them first, a lot of work, but AI can help 😏).&lt;/li&gt;
&lt;li&gt;Use business, team, enterprise, or approved API terms for proprietary code.&lt;/li&gt;
&lt;li&gt;Use personal consumer accounts only for learning, public code, or explicitly approved use cases.&lt;/li&gt;
&lt;li&gt;Do not send secrets, credentials, production data, customer data, or sensitive personal data.&lt;/li&gt;
&lt;li&gt;Human review of every generated diff.&lt;/li&gt;
&lt;li&gt;Treat AI-generated tests as useful, but not as proof that the implementation is correct.&lt;/li&gt;
&lt;li&gt;Control command execution, browser, network, MCP, plugin, and connector access.&lt;/li&gt;
&lt;li&gt;Track cost and usage during rollout.&lt;/li&gt;
&lt;li&gt;Review the policy every quarter because the tools change too quickly.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is not perfect, but it is better than either "everything is forbidden" or "everybody can use whatever they want".&lt;/p&gt;

&lt;h2&gt;
  
  
  My Personal Verdict
&lt;/h2&gt;

&lt;p&gt;For individual developers, my recommendation is simple: use personal subscriptions for learning, experiments, public code, and approved professional work. But do not assume the same subscription is automatically fine for proprietary enterprise code.&lt;/p&gt;

&lt;p&gt;For companies, I would not start with a blanket ban. I would start with the safest option developers will actually use: one or two approved business/enterprise tools, a clear data policy, and a serious pilot.&lt;/p&gt;

&lt;p&gt;For policy work, I would not try to predict every possible AI use case. I would define a few safe paths, educate developers, and review the setup every quarter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agentic Engineering Workshop
&lt;/h2&gt;

&lt;p&gt;Everything in this post is really an argument for treating agentic coding as a system, not a tool: the model, the app, my &lt;em&gt;Angular&lt;/em&gt; Guardrails, my &lt;em&gt;Angular&lt;/em&gt; Coding Style Guide, the review workflow – and yes, the data policy – all have to fit together, or the whole thing leaks somewhere.&lt;/p&gt;

&lt;p&gt;If you want to use agentic coding safely with proprietary &lt;em&gt;Angular&lt;/em&gt; code – with clear data flows, real guardrails, and a policy your developers will actually follow instead of working around – join our &lt;strong&gt;Agentic Engineering Workshop&lt;/strong&gt;, available in English and German. It takes advanced &lt;em&gt;Angular&lt;/em&gt; developers from vibe coding to traceable Agentic Engineering workflows: AI-ready project setup, guardrails, spec-first and plan-first workflows, UX and component prototyping, code review, testing, and brownfield refactoring.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🤖 &lt;a href="https://www.angulararchitects.io/en/training/agentic-engineering-von-vibe-coding-zu-professionellen-ki-gestuetzten-workflows/" rel="noopener noreferrer"&gt;&lt;strong&gt;Agentic Engineering Workshop&lt;/strong&gt;&lt;/a&gt; – 2 days, remote&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;AI coding tools are not just chat boxes. They can read files, search repositories, inspect diffs, run commands, see terminal output, connect to other systems and resolve nasty merge conflicts. That is what makes them useful, but it is also why privacy and governance matter.&lt;/p&gt;

&lt;p&gt;For individual developers, the practical rule is simple: do not send secrets, production data, customer data, or regulated personal data unless the setup explicitly allows it. For companies, the practical rule is also simple: &lt;strong&gt;do not only say no&lt;/strong&gt;. Approve a few safe paths, define clear boundaries, and make the official workflow better than the unofficial workaround.&lt;/p&gt;

&lt;p&gt;Because the teams that learn to use agentic coding professionally – safely, on approved paths instead of workarounds – will simply outpace the teams that ban it. That gap grows quietly, until falling behind turns into going broke.&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://www.angulararchitects.io/blog/ai-personal-verdict/" rel="noopener noreferrer"&gt;personal verdict&lt;/a&gt;, I pull all the threads of the original arc together – models, harnesses, costs, and privacy – into the agentic coding setup I would actually choose.&lt;/p&gt;

&lt;p&gt;Thank you for reading 🙏 this blog post was written by &lt;a href="https://alex.thalhammer.name" rel="noopener noreferrer"&gt;Alexander Thalhammer&lt;/a&gt;. For feedback, remarks or questions, please reach out to me ❤️&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>agents</category>
      <category>angular</category>
    </item>
    <item>
      <title>Agentic Engineering: What Does AI Coding Really Cost?</title>
      <dc:creator>Alexander Thalhammer</dc:creator>
      <pubDate>Thu, 04 Jun 2026 12:12:26 +0000</pubDate>
      <link>https://dev.to/lxt/agentic-engineering-what-does-ai-coding-really-cost-4kjk</link>
      <guid>https://dev.to/lxt/agentic-engineering-what-does-ai-coding-really-cost-4kjk</guid>
      <description>&lt;p&gt;In my &lt;a href="https://www.angulararchitects.io/blog/best-llms-for-angular/" rel="noopener noreferrer"&gt;first post&lt;/a&gt; of this series arc, I wrote about the LLMs I currently like to use for &lt;em&gt;Angular&lt;/em&gt; development. In the &lt;a href="https://www.angulararchitects.io/blog/ai-apps-harnesses-for-angular/" rel="noopener noreferrer"&gt;second post&lt;/a&gt;, I looked at the apps and harnesses around those models: Codex, Claude Desktop app, Cursor, Antigravity, VS Code, WebStorm, and a few more. This third part is about the annoying but important topic: money.&lt;/p&gt;

&lt;p&gt;Actually, money is only one part of the story. The other part is what data we share with these tools and which setups companies can actually use. I first had all of that in this post too, but it became too much for one article. So this part stays focused on costs, and I will cover the data and policy side in the &lt;a href="https://www.angulararchitects.io/blog/ai-data-privacy-for-angular/" rel="noopener noreferrer"&gt;next post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And yeah, spoiler: if one of the subsidized subscriptions (or in my case all four of them) works for your setup, that is by far &lt;strong&gt;the best deal&lt;/strong&gt; you can currently get. Before I dive in, let me be clear about one thing: I am not trying to sell you anything here, except, well, my brand new &lt;a href="https://www.angulararchitects.io/en/training/agentic-engineering-von-vibe-coding-zu-professionellen-ki-gestuetzten-workflows/" rel="noopener noreferrer"&gt;Agentic Engineering Workshop&lt;/a&gt; 😅. So this post is primarily about informing you about the options that actually exist, so you can make your own decision.&lt;/p&gt;

&lt;p&gt;One thought runs through this whole post, so let me say it up front: what matters in the end is not the price per token. It is the &lt;strong&gt;cost per accepted, reviewed, merged change&lt;/strong&gt;. More on that later.&lt;/p&gt;

&lt;p&gt;There is also a bigger reason I keep writing about this. In my opinion, too many European teams are still too slow to adopt agentic coding seriously – and that gap will only get bigger. So please help me spread the word, and share these posts with your friends and colleagues!&lt;/p&gt;

&lt;h2&gt;
  
  
  This Is Still Not a Benchmark
&lt;/h2&gt;

&lt;p&gt;Same disclaimer as in the previous two posts, just shorter this time: this is my current opinion as of June 1, 2026, from real &lt;em&gt;Angular&lt;/em&gt; work and a lot of time spent playing around in Codex and the Claude Desktop app – not a scientific benchmark, not procurement advice.&lt;/p&gt;

&lt;p&gt;I also spend some time in Cursor because it is a serious candidate – even more so since &lt;a href="https://apnews.com/article/582e7606e695320a299e4902dbb2704f" rel="noopener noreferrer"&gt;Elon is going to buy Cursor&lt;/a&gt; later this year (I bet he will), and I want to keep checking where it is going. Antigravity, on the other hand, is more of a tool I test from time to time. I don't use it seriously for my daily &lt;em&gt;Angular&lt;/em&gt; work yet, because it still does not feel mature enough compared with Codex, Claude Desktop app, and lately also Cursor.&lt;/p&gt;

&lt;p&gt;The subscription prices themselves have been pretty stable so far. What changes more often is what you actually get: model access, &lt;strong&gt;included usage&lt;/strong&gt;, rate limits, fast modes, and sometimes the hidden routing behind the scenes. For API and enterprise setups, longer agent runs can also change the real cost directly.&lt;/p&gt;

&lt;p&gt;So if you read this in a few months, check the linked pricing pages again. The included usage and enterprise/API details might have changed. One more note on the numbers: the providers list their prices in US dollars, so the euro figures in this post are approximate, not the exact amounts on your invoice. But the general shape of the cost problem will probably stay the same:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;subsidized &lt;strong&gt;subscriptions are amazing value&lt;/strong&gt; when they fit your setup&lt;/li&gt;
&lt;li&gt;API pricing is much more transparent, but can get &lt;strong&gt;expensive quickly&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;enterprise plans cost more, but also solve a different problem&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  TL;DR: Start With €40 or €60, Upgrade Only Where You Hit Limits
&lt;/h2&gt;

&lt;p&gt;If you are an individual developer, freelancer, trainer, consultant, or working in a setup where these tools are available, I would not start with one tool. If you have no access yet, I would buy at least the €20 subscription from OpenAI and the €20 subscription from Anthropic. That is €40/month, and for me that is currently the most useful starting point.&lt;/p&gt;

&lt;p&gt;If you can afford another €20, I would also add Cursor for a month. Then you are at €60/month and you can compare not only the models, but also the apps around them: Codex, Claude Desktop app, and Cursor. That is much more useful than reading another benchmark table.&lt;/p&gt;

&lt;p&gt;The current pattern is roughly this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;€40/month&lt;/strong&gt;: OpenAI Plus and Anthropic Pro, the minimum setup I would recommend for serious experiments&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;€60/month&lt;/strong&gt;: add Cursor Pro if you want to compare a third strong app workflow&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;+€80/month per provider&lt;/strong&gt;: upgrade OpenAI or Anthropic from the €20 tier to the €100 tier when that specific tool becomes your bottleneck&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;+€100/month more after that&lt;/strong&gt;: move the same provider from the €100 tier to the €200 tier when you really need the 20x-style heavy-user tier&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cursor upgrades&lt;/strong&gt;: separate ladder, with €20 Pro, €60 Pro+, €200 Ultra, and €40/user/month Teams&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enterprise&lt;/strong&gt;: different billing, contracts, usage pools, overages, and usually much more expensive&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are new to agentic coding, don't worry about the higher tiers on day one. Start with the base subscriptions, learn the apps, and upgrade only where you actually hit limits. That is what I did too.&lt;/p&gt;

&lt;p&gt;Today my setup is heavier: OpenAI and Anthropic on the €100 tier, Cursor and Google both on the €20 tier. I don't need all of them every day, but I want to compare them quickly because this space changes so fast.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Subscriptions Are Such a Good Deal
&lt;/h2&gt;

&lt;p&gt;These subscriptions are not just API pricing with a nicer UI. They are subsidized product bundles, and agentic coding involves a lot of hidden work: file reads, searches, tool calls, tests, terminal output, diffs, edits, and context compaction. At raw API prices, you would notice that quickly.&lt;/p&gt;

&lt;p&gt;That is why subscriptions are so attractive. The providers want developers in their ecosystem, and right now we benefit from that. But the catch is important: these plans are usually meant for humans using the product, not as a cheap backend for your company, CI pipeline, or SaaS product. In larger companies, personal consumer subscriptions also do not match procurement, billing, reporting, and cost control.&lt;/p&gt;

&lt;p&gt;So the real first question is not:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which model is cheapest per token?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The real first cost question is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can we use the subsidized app subscription, or do we need a business, enterprise, or API setup?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That one question can easily change the cost calculation by a &lt;strong&gt;factor of 10&lt;/strong&gt; or more.&lt;/p&gt;

&lt;h2&gt;
  
  
  The €20 Tier Is for Getting Started
&lt;/h2&gt;

&lt;p&gt;For me, the €20 tier is not only about "cheap access to a model". It is the entry ticket into the whole product around the model. That is why I would not buy only one subscription if I had no access yet. I would buy OpenAI and Anthropic first, and maybe Cursor too.&lt;/p&gt;

&lt;p&gt;The important comparison is not only GPT versus Claude versus Composer or Gemini. The important comparison is Codex versus Claude Desktop app versus Cursor. These are the super apps where the real workflow happens: repository search, file editing, tool calls, terminal output, diffs, review, cloud tasks, local tasks, and all the little product decisions that make an agent useful or annoying.&lt;/p&gt;

&lt;p&gt;So my recommendation is simple: spend €40 or €60 for one month and run the &lt;strong&gt;same real &lt;em&gt;Angular&lt;/em&gt; tasks through the tools&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;migrate a component to signal-based &lt;code&gt;input()&lt;/code&gt; and &lt;code&gt;output()&lt;/code&gt;, and rewrite a template from &lt;code&gt;*ngIf&lt;/code&gt; and &lt;code&gt;*ngFor&lt;/code&gt; to &lt;code&gt;@if&lt;/code&gt; and &lt;code&gt;@for&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;add tests for a service with a few dependencies&lt;/li&gt;
&lt;li&gt;ask for a code review of a real pull request&lt;/li&gt;
&lt;li&gt;try a Git workflow like rebasing one branch onto another and resolving a pile of merge conflicts&lt;/li&gt;
&lt;li&gt;describe a bug by explaining the current behavior and the desired behavior, and ask the agent to find and fix it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then look at the diffs, the review experience, the terminal usage, the verification, and how much steering you had to do. After two or three evenings, you will usually know much more than any pricing table can tell you.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Current Pricing Picture
&lt;/h2&gt;

&lt;p&gt;Let's look at that pricing table anyway, but only briefly. Again, please check the official pages before you make a real decision, because these numbers are moving targets.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F64uf6j76ppbu2pq2vxqc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F64uf6j76ppbu2pq2vxqc.png" alt="Costs of Coding Agent Subscription" width="800" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nothing special to see here. Roughly all three Frontier Labs – OpenAI, Anthropic, and Cursor (SpaceXAI) – have the same pricing, with the minor exception of Cursor offering you the 3x plan for €60.&lt;/p&gt;

&lt;h3&gt;
  
  
  OpenAI / Codex
&lt;/h3&gt;

&lt;p&gt;For Codex, check the &lt;a href="https://developers.openai.com/codex/pricing/" rel="noopener noreferrer"&gt;Codex pricing page&lt;/a&gt; and &lt;a href="https://chatgpt.com/pricing/" rel="noopener noreferrer"&gt;ChatGPT pricing page&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Anthropic / Claude
&lt;/h3&gt;

&lt;p&gt;For Claude Code, check Anthropic's &lt;a href="https://claude.com/pricing" rel="noopener noreferrer"&gt;Claude pricing page&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cursor
&lt;/h3&gt;

&lt;p&gt;For Cursor, check the &lt;a href="https://cursor.com/pricing" rel="noopener noreferrer"&gt;Cursor pricing page&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Google / Gemini / Antigravity
&lt;/h3&gt;

&lt;p&gt;For Google, check the &lt;a href="https://gemini.google/us/subscriptions/" rel="noopener noreferrer"&gt;Gemini subscriptions page&lt;/a&gt; and the &lt;a href="https://blog.google/products-and-platforms/products/google-one/google-ai-subscriptions/" rel="noopener noreferrer"&gt;Google AI subscription update&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Copilot
&lt;/h3&gt;

&lt;p&gt;Meh, I would not build a 2026 setup around Copilot anymore. And the pricing story is getting weaker too: GitHub has &lt;a href="https://github.blog/news-insights/company-news/github-copilot-is-moving-to-usage-based-billing/" rel="noopener noreferrer"&gt;moved Copilot to usage-based billing&lt;/a&gt; today (June 1, 2026), replacing premium request units with AI Credits. So Microsoft's heavy subsidizing of frontier-model usage inside Copilot is over. On top of that, the frontier LLMs are being limited more and more in Copilot. But the bigger point is simple: Copilot is just &lt;strong&gt;not the best&lt;/strong&gt; harness for agentic coding. If you're forced to use it in your company, try starting a revolution. In the end, you might just be securing your employer's survival. Hopefully this post series can support you with that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Team Plans Sit in the Middle
&lt;/h2&gt;

&lt;p&gt;Between the personal subscriptions above and the enterprise contracts below, both vendors offer a team tier that keeps the per-seat price low while adding pooled usage, central billing, and admin controls.&lt;/p&gt;

&lt;p&gt;On OpenAI's side, the Team plan comes in two tiers: a standard tier at about &lt;strong&gt;€20 per seat and month&lt;/strong&gt; and a premium tier at about &lt;strong&gt;€100 per seat and month&lt;/strong&gt; with much more included usage – the same ladder as the personal plans. Both bundle Codex, company knowledge, SSO/MFA, and data that is never used for training. See &lt;a href="https://openai.com/business/chatgpt-pricing/" rel="noopener noreferrer"&gt;ChatGPT Business pricing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj57la2ui7sdxsmz1b6ty.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj57la2ui7sdxsmz1b6ty.png" alt="AI Cost Team Plans" width="800" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Anthropic's Team plan (for teams of 5 to 150) mirrors those two tiers: &lt;strong&gt;€20 or €100 per seat and month&lt;/strong&gt;, again depending on how much included usage your developers need. Both tiers include Claude Code and Desktop, and the team gets the full agentic tooling, SSO, and "no model training". For the details, see &lt;a href="https://claude.com/pricing#team-&amp;amp;-enterprise" rel="noopener noreferrer"&gt;Claude team pricing&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enterprise Is a Different Game
&lt;/h2&gt;

&lt;p&gt;It is easy to look at a €20 subscription and ask why a company should pay so much more. I get that reaction, but enterprise is not "more Plus". It is a different cost model: seats, pooled usage, overages, central billing, usage reports, admin controls, support, and predictable invoices.&lt;/p&gt;

&lt;p&gt;The real question is what happens when 50 developers use the tool every day. Tight limits can turn cheap plans into overages, waiting time, retrying, and cleanup.&lt;/p&gt;

&lt;p&gt;So I would start with a small pilot. Give a few strong developers two paid setups for four weeks, use them on real &lt;em&gt;Angular&lt;/em&gt; work, and measure cost, accepted changes, review time, PR cycle time, and limits.&lt;/p&gt;

&lt;h2&gt;
  
  
  API Usage: Paying the Real Cost
&lt;/h2&gt;

&lt;p&gt;Subscriptions and enterprise plans hide cost. API usage does not. The official &lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI API pricing page&lt;/a&gt; and &lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Claude API pricing docs&lt;/a&gt; currently list USD prices. Converted roughly for comparison, &lt;a href="https://openai.com/index/introducing-gpt-5-5/" rel="noopener noreferrer"&gt;GPT 5.5&lt;/a&gt; is about €5 per million input tokens and €30 per million output tokens, while &lt;a href="https://www.anthropic.com/news/claude-opus-4-8" rel="noopener noreferrer"&gt;Claude Opus 4.8&lt;/a&gt; is about €5 input and €25 output.&lt;/p&gt;

&lt;p&gt;Per million tokens that sounds manageable, and output tokens are usually much more expensive than input tokens.&lt;/p&gt;

&lt;p&gt;Here is the current API price snapshot I would use for agentic coding discussions. Most official pricing pages still quote USD, so treat this as a practical comparison and check billing currency, VAT, and enterprise discounts before you make a procurement decision.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzox6xiuiue1k1ivpqrol.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzox6xiuiue1k1ivpqrol.png" alt="LLM API Model Pricing" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All prices are per million tokens, sourced from each provider's API pricing. The table is deliberately simple: no batch discount, no extra tool-call fees, no VAT, no currency conversion, no enterprise discount.&lt;/p&gt;

&lt;p&gt;For IDE-heavy teams, BYOK (bring your own key) can be interesting, no matter whether you live in &lt;a href="https://www.jetbrains.com/webstorm/" rel="noopener noreferrer"&gt;WebStorm&lt;/a&gt; or &lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;VS Code&lt;/a&gt;. On the JetBrains side, the &lt;a href="https://www.jetbrains.com/help/ai-assistant/licensing-and-subscriptions.html" rel="noopener noreferrer"&gt;JetBrains AI plans&lt;/a&gt; and &lt;a href="https://junie.jetbrains.com/docs/byok.html" rel="noopener noreferrer"&gt;Junie BYOK docs&lt;/a&gt; allow it, and most VS Code AI extensions offer the same idea: keep the IDE, connect provider keys, pay the provider.&lt;/p&gt;

&lt;p&gt;But the catch is how many tokens an agent actually moves per task – and that is the real cost driver I look at next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Token Usage Is the Real Cost Driver
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://help.openai.com/en/articles/4936856-what-are-tokens-and-how-to-count-them" rel="noopener noreferrer"&gt;OpenAI's help page on tokens&lt;/a&gt; says one token is roughly four characters of English text. For a coding agent, 200,000 input tokens are not some crazy edge case. Repository instructions, tool schemas, file excerpts, diffs, terminal output, and chat history pile up fast. &lt;a href="https://platform.claude.com/docs/en/about-claude/pricing#tool-use-pricing" rel="noopener noreferrer"&gt;Anthropic's pricing docs&lt;/a&gt; say the quiet part too: tool names, schemas, calls, and results are billed.&lt;/p&gt;

&lt;p&gt;At current API prices, one turn with 200,000 input tokens and 20,000 output tokens costs roughly €1.60 on GPT 5.5 and €1.50 on Claude Opus 4.8. Fine once. Annoying after 20 turns. Expensive if the agent starts doing laps.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://platform.claude.com/docs/en/build-with-claude/prompt-caching" rel="noopener noreferrer"&gt;Prompt caching&lt;/a&gt; helps, but it does not make output cheap and it definitely does not fix a messy workflow. So I would not worship token price. A more expensive model can still be cheaper if it gets the work done in fewer turns.&lt;/p&gt;

&lt;p&gt;This is also why I don't care too much about raw tokens per second. A model can stream quickly and still feel slow if it needs too many turns, too many tool calls, or too much reasoning to finish the task. For agentic coding, the practical speed metric is end-to-end time until I have a reviewed diff. A slower-streaming model can still be faster if it gets to the right files quickly and produces less unnecessary output.&lt;/p&gt;

&lt;p&gt;So the number that matters is not cost per token. It is &lt;strong&gt;cost per accepted, reviewed, merged change&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F462tz89t9of0x8qalx56.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F462tz89t9of0x8qalx56.png" alt="AI Cost Comparison Chart" width="799" height="541"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The DeepSWE benchmark is the one I currently trust the most. Higher percentages mean stronger benchmark results; further right means better cost efficiency. Sadly it's missing Composer due to the lack of a public API.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feyd3a3ondujau2o8l4p4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feyd3a3ondujau2o8l4p4.png" alt="LLM Costs Per Task" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This second chart – based on Artificial Analysis' coding-agent runs, which can test Composer through Cursor's own harness – includes Composer, and here it is the most efficient model by far, the best ROI in this comparison. Definitely an option to watch for API usage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Low, Medium, High, Extra
&lt;/h2&gt;

&lt;p&gt;If token volume is the real cost, the reasoning level is the first dial that changes it. Most tools now have some reasoning setting: low, medium, high, extra, max, and so on. The rule is simple: use the cheapest level that reliably solves the task.&lt;/p&gt;

&lt;p&gt;For &lt;em&gt;Angular&lt;/em&gt; work: low for tiny edits, medium for normal implementation, high for tricky bugs, component creation and refactorings, and extra/Pro/Max when a bad result would cost much more than the model usage.&lt;/p&gt;

&lt;p&gt;In practice, I often just use &lt;strong&gt;High&lt;/strong&gt; as my default (for GPT and Opus), because it is usually good enough and I don't want to waste time tuning. I know I will get hate for this, but the truth is that my time is precious and I don't care as much since I can use the subsidized subscription anyway. And I still have the option to upgrade my sub if necessary 😏&lt;/p&gt;

&lt;h2&gt;
  
  
  Fast Mode
&lt;/h2&gt;

&lt;p&gt;Reasoning level is one dial; fast mode is the other. Fast mode is interesting because speed matters. If an agent is too slow, I lose focus. But fast mode is also a cost feature. In Codex, &lt;a href="https://developers.openai.com/codex/pricing/" rel="noopener noreferrer"&gt;speed configurations increase credit consumption&lt;/a&gt;. In the Claude API, &lt;a href="https://platform.claude.com/docs/en/build-with-claude/fast-mode" rel="noopener noreferrer"&gt;Fast mode&lt;/a&gt; gives faster Opus responses at a premium.&lt;/p&gt;

&lt;p&gt;So I would definitely try it, experiment with it, and if your time is really precious, just use it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Would Control Costs in a Team
&lt;/h2&gt;

&lt;p&gt;Cost control is mostly &lt;strong&gt;workflow control&lt;/strong&gt;. I would define simple rules from the beginning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;start with medium reasoning for normal implementation work&lt;/li&gt;
&lt;li&gt;use high/extra only when the task deserves it&lt;/li&gt;
&lt;li&gt;keep tasks focused&lt;/li&gt;
&lt;li&gt;start a fresh thread when the context becomes messy&lt;/li&gt;
&lt;li&gt;keep project instructions short and useful&lt;/li&gt;
&lt;li&gt;watch terminal output size&lt;/li&gt;
&lt;li&gt;keep humans responsible for the final review&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is not only about saving money. Agents are better when the task is clear.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agentic Engineering Workshop
&lt;/h2&gt;

&lt;p&gt;And here is the uncomfortable truth about cost control: the token price is the wrong dial. The model, the app, my &lt;em&gt;Angular&lt;/em&gt; Guardrails, my &lt;em&gt;Angular&lt;/em&gt; Coding Style Guide, and the review workflow together decide what an accepted change really costs you – and that system is something you can train.&lt;/p&gt;

&lt;p&gt;If you want to keep AI coding costs under control and optimize for &lt;strong&gt;cost per accepted, reviewed, merged change&lt;/strong&gt; instead of price per token – join our &lt;strong&gt;Agentic Engineering Workshop&lt;/strong&gt;, available in English and German. There, advanced &lt;em&gt;Angular&lt;/em&gt; developers move from vibe coding to traceable Agentic Engineering workflows: AI-ready project setup, guardrails, spec-first and plan-first workflows, UX and component prototyping, code review, testing, and brownfield refactoring.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🤖 &lt;a href="https://www.angulararchitects.io/en/training/agentic-engineering-von-vibe-coding-zu-professionellen-ki-gestuetzten-workflows/" rel="noopener noreferrer"&gt;&lt;strong&gt;Agentic Engineering Workshop&lt;/strong&gt;&lt;/a&gt; – 2 days, remote&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The cheapest serious setup today is still a &lt;strong&gt;subsidized individual subscription&lt;/strong&gt;. If you have no access yet, I would start with OpenAI and Anthropic for roughly €40/month, and maybe add Cursor for another €20 for one month. That gives you Codex, Claude Desktop app, and Cursor on real &lt;em&gt;Angular&lt;/em&gt; tasks, which is much more useful than reading another benchmark table.&lt;/p&gt;

&lt;p&gt;Then keep what actually changes your daily workflow. If you hit limits, upgrade the tool that is limiting you: OpenAI or Anthropic from €20 to €100, and only then maybe to €200 for serious heavy use. For companies, the answer may be business or enterprise seats, pooled credits, API usage, or an IDE-based setup with your own provider keys – WebStorm with &lt;a href="https://www.jetbrains.com/ai/ai-assistant-features/" rel="noopener noreferrer"&gt;AI Assistant&lt;/a&gt; and Junie, or VS Code with its AI extensions.&lt;/p&gt;

&lt;p&gt;So my recommendation is not "buy tool X". Build a small, serious AI coding workflow now, measure &lt;strong&gt;cost per accepted, reviewed, merged change&lt;/strong&gt;, and keep humans responsible for quality.&lt;/p&gt;

&lt;p&gt;Teams that learn this professionally will not only write code faster. They will turn the same budget into far more accepted, reviewed, merged changes – and that advantage compounds every month.&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://www.angulararchitects.io/blog/ai-data-privacy-for-angular/" rel="noopener noreferrer"&gt;next post&lt;/a&gt;, I will look at the other half of the question: what we actually share with AI coding tools, and how I would think about privacy, retention, EU regulation, and company policy. In the &lt;a href="https://www.angulararchitects.io/blog/ai-personal-verdict/" rel="noopener noreferrer"&gt;personal verdict&lt;/a&gt;, I pull models, harnesses, costs, and privacy together.&lt;/p&gt;

&lt;p&gt;Thank you for reading 🙏 this blog post was written by &lt;a href="https://alex.thalhammer.name" rel="noopener noreferrer"&gt;Alexander Thalhammer&lt;/a&gt;. For feedback, remarks or questions, please reach out to me ❤️&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>llm</category>
      <category>angular</category>
    </item>
    <item>
      <title>Agentic: Which App/Harness Is Best for Angular Development?</title>
      <dc:creator>Alexander Thalhammer</dc:creator>
      <pubDate>Tue, 02 Jun 2026 07:14:46 +0000</pubDate>
      <link>https://dev.to/lxt/agentic-which-appharness-is-best-for-angular-development-28h9</link>
      <guid>https://dev.to/lxt/agentic-which-appharness-is-best-for-angular-development-28h9</guid>
      <description>&lt;p&gt;In my &lt;a href="https://www.angulararchitects.io/blog/best-llms-for-angular/" rel="noopener noreferrer"&gt;last post&lt;/a&gt;, published yesterday (on May 26, 2026), I wrote about which LLMs I currently like to use for &lt;em&gt;Angular&lt;/em&gt; development and promised a follow-up about the apps and harnesses around them. This is that follow-up.&lt;/p&gt;

&lt;p&gt;Chronologically, this is the second post in the original series arc. The follow-ups look at &lt;a href="https://www.angulararchitects.io/blog/ai-costs-for-angular/" rel="noopener noreferrer"&gt;costs&lt;/a&gt; and the &lt;a href="https://www.angulararchitects.io/blog/ai-data-privacy-for-angular/" rel="noopener noreferrer"&gt;data and privacy side&lt;/a&gt; of agentic coding, before the &lt;a href="https://www.angulararchitects.io/blog/ai-personal-verdict/" rel="noopener noreferrer"&gt;personal verdict&lt;/a&gt; pulls the first arc together.&lt;/p&gt;

&lt;h2&gt;
  
  
  From Models to Agent Harnesses
&lt;/h2&gt;

&lt;p&gt;Picking the model is only half the picture. The same &lt;a href="https://www.anthropic.com/news/claude-opus-4-7" rel="noopener noreferrer"&gt;Opus 4.7&lt;/a&gt; behaves very differently in a JetBrains chat window, in &lt;a href="https://www.anthropic.com/product/claude-code" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt; on the terminal, or wrapped in &lt;a href="https://cursor.com/download/" rel="noopener noreferrer"&gt;Cursor&lt;/a&gt;'s agent mode. The harness decides how the model sees your code, how it edits files, how it runs tools, how much it verifies, and whether it feels like autocomplete, a helpful assistant, or a real agent. For serious &lt;em&gt;Angular&lt;/em&gt; work, that part matters &lt;strong&gt;at least as much as the underlying model&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So this post is about the other half: the apps, IDE integrations, and agentic coding tools I've used over the last few months. I'll move from classic Copilot-style autocomplete to IDE agents and today's super apps, and then explain where Codex, Claude Desktop app, Cursor, Antigravity, VS Code, and WebStorm currently fit (or don't) into my daily &lt;em&gt;Angular&lt;/em&gt; workflow. I also want to look a little bit at the product philosophy behind these tools, because that often explains the day-to-day experience better than a feature checklist.&lt;/p&gt;

&lt;h2&gt;
  
  
  This Is Not a Benchmark
&lt;/h2&gt;

&lt;p&gt;Before we go any further, one disclaimer: this is my &lt;strong&gt;subjective opinion&lt;/strong&gt; from daily &lt;em&gt;Angular&lt;/em&gt; work – projects, workshops, code reviews, refactorings – not a scientific ranking. Your IDE, operating system, budget, company policies, and tolerance for agentic autonomy might lead you to a completely different result, so read this as a practical field report from my current setup.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnymccwebusxb8s7nrrki.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnymccwebusxb8s7nrrki.png" alt="Apps/Harnesses Overview" width="800" height="536"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The sketch above is a very simplified overview of how I see these tools in terms of capabilities and my personal preferences – a visual aid, not an objective comparison.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR: My Current Setup
&lt;/h2&gt;

&lt;p&gt;If you only have thirty seconds: my daily drivers for &lt;em&gt;Angular&lt;/em&gt; work are currently &lt;strong&gt;Codex&lt;/strong&gt; (with GPT 5.5) and the &lt;strong&gt;Claude Desktop app&lt;/strong&gt; (with Opus 4.7 – meanwhile 4.8, see the conclusion), used roughly interchangeably. Codex has the more polished app experience, while the Claude Desktop app gives me access to the Opus models I trust most for architecture, design, and larger refactorings. &lt;strong&gt;Cursor&lt;/strong&gt; with Composer 2.5 is a strong third option, especially when speed, cost, IDE integration, or cloud agents matter. &lt;strong&gt;Antigravity&lt;/strong&gt; is interesting but, in my opinion, not yet on the same level. And whenever I do real handcrafting, I still happily switch back to &lt;strong&gt;WebStorm&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;All of this assumes a strict human-in-the-loop workflow: &lt;strong&gt;I review every diff&lt;/strong&gt;, and I expect every line of generated code to look as if I had written it myself.&lt;/p&gt;

&lt;h2&gt;
  
  
  My AI Coding Journey
&lt;/h2&gt;

&lt;p&gt;To explain where I ended up, I first need to show the path that got me there. I'll start with the more traditional IDE integrations and then move on to the newer agentic coding apps.&lt;/p&gt;

&lt;h3&gt;
  
  
  WebStorm + GitHub Copilot
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm7744vj5gm0v0gul9bsz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm7744vj5gm0v0gul9bsz.png" alt="WebStorm by JetBrains" width="488" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For over a decade PhpStorm (starting in my WordPress era) and later &lt;a href="https://www.jetbrains.com/webstorm/" rel="noopener noreferrer"&gt;WebStorm&lt;/a&gt; have been my main IDEs for web development. So when &lt;a href="https://github.com/features/copilot" rel="noopener noreferrer"&gt;GitHub Copilot&lt;/a&gt; launched, it was a natural choice to try it out in WebStorm. It was one of the first AI coding tools I used, and it had a big impact on how I thought about AI-assisted coding.&lt;/p&gt;

&lt;p&gt;I just looked up in my GitHub &lt;a href="https://github.com/account/billing/history" rel="noopener noreferrer"&gt;Billing History&lt;/a&gt; and noticed that I've been using Copilot since 2023-10-27. That's exactly 31 months ago. What a journey. For the most part of that time – let's say the first 25 months, I used GitHub Copilot in WebStorm (and &lt;a href="https://code.visualstudio.com/download" rel="noopener noreferrer"&gt;VS Code&lt;/a&gt;), which was a great way to get started with AI-assisted coding. It felt like a natural extension of the IDE's existing autocomplete features, and it worked well for small code snippets and boilerplate.&lt;/p&gt;

&lt;p&gt;However, it also had its limitations. It often struggled with larger code contexts, complex &lt;em&gt;Angular&lt;/em&gt; patterns, and multi-file refactorings. In particular, it was usually &lt;strong&gt;a step or two behind modern &lt;em&gt;Angular&lt;/em&gt; APIs&lt;/strong&gt;: it kept suggesting &lt;code&gt;NgModule&lt;/code&gt;-based code long after standalone components had become the default, leaned on &lt;code&gt;*ngIf&lt;/code&gt; and &lt;code&gt;*ngFor&lt;/code&gt; instead of the new control flow syntax, and rarely reached for signals, &lt;code&gt;input()&lt;/code&gt; or &lt;code&gt;output()&lt;/code&gt; on its own. It was more of a helper for writing code than a partner for driving larger coding workflows. And sometimes it just was pretty annoying, suggesting irrelevant or incorrect code that I had to manually reject.&lt;/p&gt;

&lt;p&gt;In 2025, I also experimented quite a bit with Cursor. However, I still didn't like the VS Code clone too much and preferred to stay in my beloved JetBrains editors.&lt;/p&gt;

&lt;h3&gt;
  
  
  WebStorm + AI Assistant + Junie + Opus 4.5
&lt;/h3&gt;

&lt;p&gt;Switch to November 2025 when &lt;a href="https://www.anthropic.com/news/claude-opus-4-5" rel="noopener noreferrer"&gt;Opus 4.5&lt;/a&gt; was released. People on social platforms got pretty excited and also some of my friends pushed me to try the new model. So again, I followed the easy path and I started to use the new model through AI Assistant and Junie in WebStorm.&lt;/p&gt;

&lt;p&gt;For context: &lt;a href="https://www.jetbrains.com/ai/ai-assistant-features/" rel="noopener noreferrer"&gt;JetBrains AI Assistant&lt;/a&gt; is the AI layer built directly into JetBrains IDEs like WebStorm. It gives you the typical IDE-integrated AI features: chat, code explanations, code generation, documentation help, code completion, and smaller edits in the context of the project. The same AI Assistant interface is also becoming more of a hub for different agents and providers, so depending on your setup you can use tools like Junie, Claude, Codex or even Cursor and a lot more from inside the JetBrains workflow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.jetbrains.com/help/ai-assistant/junie-agent.html" rel="noopener noreferrer"&gt;Junie&lt;/a&gt; is JetBrains' own more agentic coding tool. Instead of only answering questions or completing snippets, it can take a task, create a plan, edit multiple files, run commands or tests if you allow it, and iterate inside the IDE.&lt;/p&gt;

&lt;p&gt;But then, since I was using &lt;a href="https://www.anthropic.com/news/claude-opus-4-6" rel="noopener noreferrer"&gt;Claude Opus 4.6&lt;/a&gt; most of the time, I wanted to experiment with different harnesses for that model. So I came to a new workflow where I used VS Code with the &lt;a href="https://marketplace.visualstudio.com/items?itemName=Anthropic.claude-code" rel="noopener noreferrer"&gt;Claude Code extension&lt;/a&gt; for my agentic coding.&lt;/p&gt;

&lt;h3&gt;
  
  
  VS Code + Claude Code + Opus 4.6
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2mzeuacol0v2z31y77lw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2mzeuacol0v2z31y77lw.png" alt="VS Code by Microsoft" width="488" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A few weeks after &lt;a href="https://www.anthropic.com/news/claude-opus-4-6" rel="noopener noreferrer"&gt;Opus 4.6&lt;/a&gt; was released to the public, I think it was around the end of February, I started to look more seriously for the best harness for that model. Until then, I mostly thought about the model itself: is it good enough, fast enough, and useful enough for real &lt;em&gt;Angular&lt;/em&gt; work? But the more I compared the different tools, the more obvious it became that &lt;strong&gt;the surrounding app matters a lot&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;VS Code was a natural candidate for that experiment. I never loved it as much as my JetBrains IDEs, but I knew it well from the work in my &lt;a href="https://www.angulararchitects.io/trainer/alexander-thalhammer/" rel="noopener noreferrer"&gt;&lt;em&gt;Angular Architects&lt;/em&gt; workshops&lt;/a&gt;, where many participants use it as their main editor. And with the &lt;a href="https://marketplace.visualstudio.com/items?itemName=Anthropic.claude-code" rel="noopener noreferrer"&gt;Claude Code extension&lt;/a&gt; in VS Code, I had the first setup where Opus really felt &lt;strong&gt;stronger because of the harness&lt;/strong&gt; around it. Suddenly Opus could read across an entire &lt;em&gt;Angular&lt;/em&gt; feature folder, follow a signal from &lt;code&gt;computed()&lt;/code&gt; back to its source, and propose multi-file edits that respected my standalone-component setup, my routing, and even my &lt;code&gt;providedIn: 'root'&lt;/code&gt; services without me having to spoon-feed it the context.&lt;/p&gt;

&lt;p&gt;That was the moment where I started to think: OK, this is &lt;strong&gt;not only about the LLM anymore&lt;/strong&gt;. Well and that was even before I tried the agentic coding apps or what I like to call them &lt;em&gt;super apps&lt;/em&gt; for the first time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beyond IDE: Agentic Coding Apps (Super Apps)
&lt;/h2&gt;

&lt;p&gt;After using VS Code for my agentic coding for about a month or so, I was ready to start a new journey.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuki5mlr2uigk3n2fumls.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuki5mlr2uigk3n2fumls.png" alt="Super App Candidates" width="800" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the screenshot above, you can see the four main apps I currently use for agentic coding: Claude Desktop app, Codex, Cursor, and Antigravity. Oh boy, they really look the same, don't they?&lt;/p&gt;

&lt;p&gt;To be clear, I strictly follow a human-in-the-loop workflow here. That is because I care a lot about &lt;strong&gt;clean code and high-quality code&lt;/strong&gt;. Some people think that the quality of the codebase does not, or will not, matter anymore in the future. I strongly disagree with that view, although I accept it as a fair point that challenges my own position.&lt;/p&gt;

&lt;p&gt;At first glance, these tools look like different skins around the same idea. But the more I use them, the more I think they are actually making &lt;strong&gt;different bets&lt;/strong&gt;. The Claude Desktop app feels terminal-first and model-first: give a strong model a lot of tool access and let it work. Codex feels app-first and verification-first: keep the interface calm, use the local environment when possible, and make the agent prove more of its work. Cursor feels IDE- and cloud-first: keep the editor close, but also make it possible to run agents somewhere else and bring the result back into the team workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Claude Desktop app + Opus 4.7
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmk1ogau6f5gkgtsdcqxu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmk1ogau6f5gkgtsdcqxu.png" alt="Claude Desktop app by Anthropic" width="488" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Whichever of these bets a tool makes, my own rule stays the same: I review everything these tools do, and I want every line of code to look exactly &lt;strong&gt;as if I had handcrafted it&lt;/strong&gt;. Since the Claude Desktop app and the VS Code extension share the same underlying Claude Code agent harness, the app already felt familiar to me. The Claude CLI is the canonical surface; the desktop app and the VS Code/JetBrains extensions are different front-ends that drive that same harness.&lt;/p&gt;

&lt;p&gt;To be honest, I have never been much of a CLI guy (nor Linux – I prefer macOS – but I love Android), so I really prefer using a fancy desktop app. But I fully understand that this is a pure personal preference. And yeah, that's all I can say to that.&lt;/p&gt;

&lt;p&gt;So basically, the first one of these super apps that I really used on a day-to-day basis was the &lt;a href="https://www.anthropic.com/product/claude-code" rel="noopener noreferrer"&gt;Claude Desktop app&lt;/a&gt;. By the way, it also offers a Co-working tab, which is supposed to be the right choice for knowledge workers, while the Code tab is the choice for us developers.&lt;/p&gt;

&lt;p&gt;The big strength of Claude Code is still the &lt;strong&gt;terminal story&lt;/strong&gt;. It met developers where many of them already live, and that is probably one reason why adoption was so fast. The downside is that this also shapes the whole product: images, rich UI, visual verification, and desktop-app polish will always feel a bit different when the canonical workflow is the CLI. I also notice that Claude Code is very willing to spend tokens if that makes the agent feel more capable, for example with subagents or larger parallel explorations. That can be great for difficult tasks, but it is something I want to keep an eye on when cost and focus matter.&lt;/p&gt;

&lt;p&gt;The newer workflow-style features make this even more extreme. Claude can split a larger task into many smaller agent runs, explore files in parallel, and compose the result back into one plan or implementation. That can be genuinely useful for broad refactorings or deep reviews, because one agent can focus on one file or concern. But it also changes the risk profile: more subagents means more tokens, more nondeterminism, and more places where a small misunderstanding can cascade. I would use this for hard, expensive problems – not for normal day-to-day edits.&lt;/p&gt;

&lt;p&gt;For &lt;em&gt;Angular&lt;/em&gt; work specifically, Claude Code with Opus 4.7 is the &lt;strong&gt;harness I trust most&lt;/strong&gt; when a task spans the whole stack: introducing a new feature module, migrating an older area to standalone components and signals, or refactoring a non-trivial RxJS pipeline into a cleaner mix of signals and &lt;code&gt;toSignal()&lt;/code&gt;. It also handles the boring-but-important parts well, like keeping &lt;code&gt;app.config.ts&lt;/code&gt;, route definitions, and lazy-loaded feature routes in sync after a rename.&lt;/p&gt;

&lt;h3&gt;
  
  
  Codex + GPT 5.5
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxajo9lpcvdoxdnv6qn5n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxajo9lpcvdoxdnv6qn5n.png" alt="Codex by OpenAI" width="488" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Remember the Claude app's split between the Code and Co-working tabs? That is already a big difference compared with the &lt;a href="https://openai.com/codex/" rel="noopener noreferrer"&gt;Codex app&lt;/a&gt; by OpenAI, because Codex does not split these workflows into separate apps. Instead, it combines coding and co-working in just one app.&lt;/p&gt;

&lt;p&gt;Again, starting to use Codex at the end of April felt familiar because it more or less had the same interface as the Claude Desktop app. But from day one, I had the feeling that there were fewer bugs and that the Codex app, and therefore the GPT harness, &lt;strong&gt;just worked&lt;/strong&gt; most of the time.&lt;/p&gt;

&lt;p&gt;What I like about Codex is that it usually feels &lt;strong&gt;less theatrical&lt;/strong&gt;. There is less visual noise, fewer little productivity animations, and more focus on the task, the diff, the terminal output, and the verification. That sounds boring, but for me boring is often good when I work on real code. I especially like the direction around using the machine and environment I already have configured instead of pretending that every project is easy to run in a clean cloud sandbox. For an &lt;em&gt;Angular&lt;/em&gt; application with local setup, browser checks, environment variables, internal packages, and maybe a slightly weird test configuration, that matters a lot.&lt;/p&gt;

&lt;p&gt;And there is another practical detail I really like: &lt;a href="https://openai.com/index/work-with-codex-from-anywhere/" rel="noopener noreferrer"&gt;Codex can now be controlled from the smartphone&lt;/a&gt; while it still uses the desktop machine as the actual working environment. So I can start or continue a task from the couch, the train, or wherever, without moving the whole project into a cloud runner. Of course, I still review the diff properly later, but for keeping an agent moving this is very convenient.&lt;/p&gt;

&lt;p&gt;So my personal verdict would be that the Codex app is &lt;strong&gt;superior&lt;/strong&gt; to the Claude Desktop app. However, the underlying models in the Claude Desktop app, Opus 4.5, 4.6, and 4.7, are still &lt;strong&gt;superior&lt;/strong&gt; to &lt;a href="https://openai.com/index/introducing-gpt-5-5/" rel="noopener noreferrer"&gt;GPT 5.5&lt;/a&gt; in many cases, especially in the fields I mentioned in the last post, such as architecture, design, and more.&lt;/p&gt;

&lt;p&gt;In day-to-day &lt;em&gt;Angular&lt;/em&gt; tasks, though, the gap is often &lt;strong&gt;smaller than the model benchmarks suggest&lt;/strong&gt;. Codex with GPT 5.5 is very strong at the kind of work that fills most of my week: writing or updating standalone components, generating Vitest or Jasmine specs that actually use &lt;code&gt;TestBed&lt;/code&gt;, scaffolding signal-based stores, and producing usable Signal Forms code. Where I still reach for the Claude Desktop app is the harder, more design-heavy work, for example shaping a new feature's component tree or untangling a legacy service.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cursor + Composer 2.5
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9t16rc0okpa8p29qq0lv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9t16rc0okpa8p29qq0lv.png" alt="Cursor" width="488" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once I decided to do a workshop about agentic coding, I realized I had to try out all the available options and not just use my favorite ones. So of course, one candidate for the best super app, or in other words, agentic coding app, is &lt;a href="https://cursor.com/download/" rel="noopener noreferrer"&gt;Cursor&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Cursor, basically a VS Code fork, was one of the first IDEs to really focus on agentic coding and agentic workflows, and many people started to use these workflows much earlier than I did. But in the past, let's say at the beginning of 2025, I had the feeling that the code generation was not really good enough for my requirements.&lt;/p&gt;

&lt;p&gt;That was then. Today, Cursor plays in a different league: it recently announced a &lt;a href="https://cursor.com/blog/spacex-model-training" rel="noopener noreferrer"&gt;major deal with SpaceXAI&lt;/a&gt; that gives SpaceXAI the option to acquire Cursor for 60 billion dollars, or to pay 10 billion dollars for their partnership instead. So this is no longer only a small IDE story.&lt;/p&gt;

&lt;p&gt;So why are Cursor and SpaceXAI such a good match? Because SpaceXAI has huge compute with &lt;a href="https://www.tomshardware.com/pc-components/gpus/nvidia-backs-20-billion-xai-chip-deal" rel="noopener noreferrer"&gt;Colossus 2&lt;/a&gt;, while Cursor has strong product usage signals and developer workflow telemetry. Together, they are about to enter, or have already entered, the frontier stage of agentic coding.&lt;/p&gt;

&lt;p&gt;By the way, &lt;a href="https://cursor.com/download/" rel="noopener noreferrer"&gt;Cursor&lt;/a&gt; and &lt;a href="https://cursor.com/changelog/composer-2-5" rel="noopener noreferrer"&gt;Composer 2.5&lt;/a&gt; are also super fast, and they can be a &lt;strong&gt;cheap alternative&lt;/strong&gt; if you really have to pay for the tokens and cannot use the subsidized OpenAI or Anthropic subscription model. But more on the costs of using these tools in &lt;a href="https://www.angulararchitects.io/blog/ai-costs-for-angular/" rel="noopener noreferrer"&gt;the next post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The part I had underestimated is &lt;strong&gt;Cursor's cloud story&lt;/strong&gt;. I used to think of Cursor mostly as the AI-first VS Code clone. That is still part of it, of course, but the more interesting direction is letting agents run away from my local machine and come back with a result. If your team wants to trigger work from Slack, run several agents in parallel, or give non-developers a safe way to start small engineering tasks, Cursor's &lt;a href="https://cursor.com/blog/cloud-agents" rel="noopener noreferrer"&gt;cloud agents&lt;/a&gt; become much more interesting than the editor alone. Of course, this also makes the &lt;a href="https://www.angulararchitects.io/blog/ai-data-privacy-for-angular/" rel="noopener noreferrer"&gt;data and policy side&lt;/a&gt; more important.&lt;/p&gt;

&lt;p&gt;For &lt;em&gt;Angular&lt;/em&gt; specifically, I found Composer 2.5 most useful for the mechanical kind of refactor: renaming a service across an entire feature, converting a class-based &lt;code&gt;@Input()&lt;/code&gt; to the new &lt;code&gt;input()&lt;/code&gt; signal API, or rewriting a template from &lt;code&gt;*ngIf&lt;/code&gt;/&lt;code&gt;*ngFor&lt;/code&gt; to the new &lt;code&gt;@if&lt;/code&gt;/&lt;code&gt;@for&lt;/code&gt; control flow. It is &lt;strong&gt;fast enough to feel interactive&lt;/strong&gt;, and that speed changes how often you actually run a refactor instead of postponing it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Antigravity + Gemini 3.5 Flash
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F02svtpefgyc3eus51wfy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F02svtpefgyc3eus51wfy.png" alt="Antigravity by Google" width="488" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A little more than a week ago (on May 19, 2026), Google also announced a new release at &lt;a href="https://blog.google/innovation-and-ai/technology/developers-tools/google-io-2026-developer-highlights/" rel="noopener noreferrer"&gt;Google I/O&lt;/a&gt;, where they presented the new agentic coding app called &lt;a href="https://antigravity.google/download" rel="noopener noreferrer"&gt;Antigravity 2.0&lt;/a&gt;. The interesting thing is that they transformed Antigravity from an IDE with an included agentic coding workflow into a super app similar to the Claude Desktop app and Codex. If you compare the user interface to Codex, it almost feels like a one-to-one copy, which is good because I think Codex currently has the &lt;strong&gt;best user interface&lt;/strong&gt; of all the options.&lt;/p&gt;

&lt;p&gt;The former IDE is now kind of an integrated sub-app that can be triggered from the Antigravity app whenever needed. This is actually a really good idea, and I like it very much. The same idea already exists in Codex, where you can open your preferred IDE. In my case, that would be WebStorm, of course. For whatever reason, the Claude Desktop app is missing that feature.&lt;/p&gt;

&lt;p&gt;So while this 2.0 release is definitely a big step in the right direction, I personally don't think Antigravity is competitive with the other options yet. There are two reasons for that. First, the app still &lt;strong&gt;feels clunky and buggy&lt;/strong&gt;, and it has fewer settings than the other apps, so it just does not feel as mature. Second, yes, you can use other models, but I think the real point of using Antigravity is to use it with &lt;a href="https://blog.google/innovation-and-ai/models-and-research/gemini-models/gemini-3-5/" rel="noopener noreferrer"&gt;Gemini models&lt;/a&gt;, especially Gemini 3.5 Flash in the new Antigravity 2.0 setup. Usage for models like Opus 4.6 has recently been reduced, and I guess it will be reduced even more in the future.&lt;/p&gt;

&lt;p&gt;On &lt;em&gt;Angular&lt;/em&gt; tasks, Gemini 3.5 Flash is fast, and that speed is useful when you want quick iterations over a whole feature module or a long template. But for my daily work it is simply &lt;strong&gt;not competitive yet&lt;/strong&gt;: in practice I still see it default to slightly older &lt;em&gt;Angular&lt;/em&gt; idioms more often than Opus 4.7 or GPT 5.5, especially around signals and the new control flow. With clear guardrails it gets there eventually, but it currently needs more steering than the other two – more than I'm willing to invest in real project work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Open Source Alternatives to the Big Ones
&lt;/h3&gt;

&lt;p&gt;Beyond the big names, I would also keep an eye on &lt;a href="https://opencode.ai/" rel="noopener noreferrer"&gt;OpenCode&lt;/a&gt; and &lt;a href="https://t3.codes/" rel="noopener noreferrer"&gt;T3 Code&lt;/a&gt;. OpenCode is a solid open-source, terminal-first option if you want a model-agnostic agent and bring your own provider setup. T3 Code is interesting for the opposite reason: it gives you an open-source GUI on top of the agents you may already pay for, like Claude Code, Codex CLI, OpenCode, or Cursor.&lt;/p&gt;

&lt;h3&gt;
  
  
  But I'm super happy with working in the terminal
&lt;/h3&gt;

&lt;p&gt;Fair enough. If you love the terminal, you can absolutely keep using it, but I no longer think it is obviously the best default for agentic coding. &lt;strong&gt;Apps make it easier&lt;/strong&gt; to review diffs, manage several agents, use screenshots or browser checks, continue from the phone, and turn work into PRs. But that is personal preference, and I don't want to force anybody into my workflow.&lt;/p&gt;

&lt;p&gt;If you are still happiest in the terminal, the &lt;a href="https://opencode.ai/" rel="noopener noreferrer"&gt;OpenCode&lt;/a&gt; I just mentioned is the option I would suggest trying first. And if you like building your own tooling, have a look at &lt;a href="https://pi.dev/" rel="noopener noreferrer"&gt;Pi&lt;/a&gt;: a minimal terminal coding harness with &lt;code&gt;AGENTS.md&lt;/code&gt; support, skills, extensions, tree-structured sessions, and many model providers. It feels less like a sealed product and more like something you can adapt to your own workflow.&lt;/p&gt;

&lt;p&gt;I go deeper on that terminal-vs-IDE-vs-super-app split – including opencode, Pi, Claude Code, Cline, and the project setup around them – in the &lt;a href="https://www.angulararchitects.io/blog/ae-harness-setup-for-angular/" rel="noopener noreferrer"&gt;harness setup post&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which Coding Agent Harness Would I Choose Today?
&lt;/h2&gt;

&lt;p&gt;Okay, that was either a lot of information to digest, or you were already familiar with all the super apps and IDEs I mentioned. So let's try to summarize the current state of my workflow and which harnesses I prefer for different tasks.&lt;/p&gt;

&lt;p&gt;My two favorite tools are definitely Codex and the Claude Desktop app. I would put them &lt;strong&gt;roughly on par&lt;/strong&gt;. I use them daily for almost everything, and I feel really lucky that I can use these tools through subsidized subscription models. More on costs in &lt;a href="https://www.angulararchitects.io/blog/ai-costs-for-angular/" rel="noopener noreferrer"&gt;the next post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;While preparing the upcoming workshop, I also found out that Cursor has really become a super app and that it supports agentic workflows very well. All three are capable of code generation, code review, modernization, refactoring, and writing tests in your &lt;em&gt;Angular&lt;/em&gt; codebases.&lt;/p&gt;

&lt;p&gt;The practical difference is not only which one can solve a given prompt. It is also &lt;strong&gt;how it tries to solve it&lt;/strong&gt;. Claude Code is great when I want to give a strong model a lot of freedom and let it explore. Codex is great when I want a calmer engineering tool that uses my local setup and keeps verification close to the work. Cursor is great when the work should move beyond my own machine into an IDE, browser, cloud, or team workflow.&lt;/p&gt;

&lt;p&gt;For Antigravity, however, I still see a lot of work for the Google teams before it reaches the level of the other three apps. And of course, those other apps will continue to improve as well.&lt;/p&gt;

&lt;p&gt;For my beloved IDE WebStorm, I see &lt;strong&gt;difficult times ahead&lt;/strong&gt;. I hope the JetBrains team will be able to catch up with the other apps in terms of productivity and usability when using these models for agentic tasks. There is still a big benefit to using IDEs, and they also allow you to choose between several model providers. Even Cursor allows you to do that, whereas Codex and the Claude Desktop app want to lock you into one LLM provider.&lt;/p&gt;

&lt;p&gt;So today, I'll still stick with Codex and the Claude Desktop app. But maybe in a month or so, I might already be switching to the next super app. We will see.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agentic Engineering Workshop
&lt;/h2&gt;

&lt;p&gt;By now you can probably guess the bigger point: the app is only one piece. The model, the harness, my &lt;em&gt;Angular&lt;/em&gt; Guardrails, my &lt;em&gt;Angular&lt;/em&gt; Coding Style Guide, and the review workflow only work as one system – and picking that system deliberately is a skill you can learn.&lt;/p&gt;

&lt;p&gt;If you want to pick the right harness for your team and build a strict human-in-the-loop workflow where every generated line of &lt;em&gt;Angular&lt;/em&gt; code looks handcrafted – join our &lt;strong&gt;Agentic Engineering Workshop&lt;/strong&gt;, available in English and German. Hands-on, advanced &lt;em&gt;Angular&lt;/em&gt; developers move from vibe coding to traceable Agentic Engineering workflows: AI-ready project setup, guardrails, spec-first and plan-first workflows, UX and component prototyping, code review, testing, and brownfield refactoring.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🤖 &lt;a href="https://www.angulararchitects.io/en/training/agentic-engineering-von-vibe-coding-zu-professionellen-ki-gestuetzten-workflows/" rel="noopener noreferrer"&gt;&lt;strong&gt;Agentic Engineering Workshop&lt;/strong&gt;&lt;/a&gt; – 2 days, remote&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The main takeaway is simple: &lt;strong&gt;the harness matters&lt;/strong&gt;. A great model in a weak app can feel surprisingly limited, while the same model in a strong agentic workflow can become much more useful for real &lt;em&gt;Angular&lt;/em&gt; work. That is why &lt;em&gt;Angular&lt;/em&gt; teams should not only ask which model is best, but also which harness, review process, and engineering workflow actually help them ship better code.&lt;/p&gt;

&lt;p&gt;For my current workflow, Codex and the Claude Desktop app are still my &lt;strong&gt;daily drivers&lt;/strong&gt;. Codex currently feels like the more polished app to me, while the Claude Desktop app gives me access to the Opus models that I trust most for architecture, design, and complex reasoning. By the way, Anthropic has since released &lt;a href="https://www.anthropic.com/news/claude-opus-4-8" rel="noopener noreferrer"&gt;Opus 4.8&lt;/a&gt; (on May 28, 2026), which only underlines the point of this post: the model underneath keeps improving, while the harness around it is what I actually commit to. Cursor deserves a serious look when speed, cost, an IDE-like workflow, or cloud agents matter, and Antigravity is worth watching, but for now it still feels like it needs more time. When I do careful handcrafting, I still switch back to WebStorm.&lt;/p&gt;

&lt;p&gt;One thing I learned quickly: don't force the same workflow onto every tool. Codex, the Claude Desktop app, Cursor, and even the smaller open-source options work best when you let them shape your workflow a little bit.&lt;/p&gt;

&lt;p&gt;So &lt;strong&gt;don't get too attached&lt;/strong&gt; to one app, one IDE, or one vendor workflow. These tools are changing too quickly, so it is worth trying new harnesses regularly and switching when they make your real project work better.&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://www.angulararchitects.io/blog/ai-costs-for-angular/" rel="noopener noreferrer"&gt;costs post&lt;/a&gt;, I look at pricing, subscriptions, and cost per accepted change. In the &lt;a href="https://www.angulararchitects.io/blog/ai-data-privacy-for-angular/" rel="noopener noreferrer"&gt;privacy post&lt;/a&gt;, I look at what these tools actually receive from your codebase and how I would think about company policy. In the &lt;a href="https://www.angulararchitects.io/blog/ai-personal-verdict/" rel="noopener noreferrer"&gt;personal verdict&lt;/a&gt;, I pull the first arc together. And in the &lt;a href="https://www.angulararchitects.io/blog/ae-harness-setup-for-angular/" rel="noopener noreferrer"&gt;harness setup post&lt;/a&gt;, I finally draw the line between &lt;em&gt;app&lt;/em&gt; and &lt;em&gt;harness&lt;/em&gt; and show how to configure one for &lt;em&gt;Angular&lt;/em&gt;, step by step.&lt;/p&gt;

&lt;p&gt;Thank you for reading 🙏 this blog post was written by &lt;a href="https://alex.thalhammer.name" rel="noopener noreferrer"&gt;Alexander Thalhammer&lt;/a&gt;. For feedback, remarks or questions, please reach out to me ❤️&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>angular</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Agentic Engineering: Which LLM Is Best for Angular Development?</title>
      <dc:creator>Alexander Thalhammer</dc:creator>
      <pubDate>Mon, 01 Jun 2026 20:40:36 +0000</pubDate>
      <link>https://dev.to/lxt/agentic-engineering-which-llm-is-best-for-angular-development-1o2</link>
      <guid>https://dev.to/lxt/agentic-engineering-which-llm-is-best-for-angular-development-1o2</guid>
      <description>&lt;h2&gt;
  
  
  My AI Coding Journey
&lt;/h2&gt;

&lt;p&gt;It's almost six months since my &lt;a href="https://www.angulararchitects.io/blog/angular-aria/" rel="noopener noreferrer"&gt;last post on this blog&lt;/a&gt;, published in early December 2025. In that time, my daily work changed rapidly and completely. Until November 2025, I thought AI was &lt;strong&gt;not useful&lt;/strong&gt; for my work in complex enterprise &lt;em&gt;Angular&lt;/em&gt; projects, where code quality has to be very high.&lt;/p&gt;

&lt;p&gt;But then &lt;a href="https://www.anthropic.com/news/claude-opus-4-5" rel="noopener noreferrer"&gt;Opus 4.5&lt;/a&gt; was released to the public on November 24, 2025. I started to experiment with it and quickly found that it could help with &lt;strong&gt;real work&lt;/strong&gt;: code generation, code review, documentation, modernization, refactoring, and a lot more.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;quality surprised me&lt;/strong&gt; first. Some generated &lt;em&gt;Angular&lt;/em&gt; code looked close to how I would have written it myself. The speed surprised me next. Naturally, I felt a bit obsolete, but it was and still is pretty exciting.&lt;/p&gt;

&lt;p&gt;The biggest change is not that the latest models write better code. The bigger shift is that using them has become &lt;strong&gt;much easier&lt;/strong&gt;: I no longer need to craft a perfect prompt upfront and hope that the result matches my intent. Instead, the interaction feels much closer to talking to a senior developer: I can describe the problem, add constraints, answer questions, clarify the spec, and let the model help discover what we actually want to build.&lt;/p&gt;

&lt;p&gt;If you've been working with AI every day for months, there might not be much new for you in this post. But if you are rather new to Agentic Engineering and want to understand which LLM is best for &lt;em&gt;Angular&lt;/em&gt; development, this post is for you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comparison from November 2025 (Outdated!)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5aei1e3ssbxry7o1fimu.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5aei1e3ssbxry7o1fimu.webp" alt="Opus 4.5 comparison from November 2025" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;BTW: SWE-bench itself is also showing its age. I will use more recent coding-agent benchmarks from Artificial Analysis later in this post.&lt;/p&gt;

&lt;p&gt;I started using Opus 4.5 through my favorite IDE, WebStorm. But in this post, I don't want to talk about IDE integration, harnesses, and tools yet. &lt;a href="https://www.angulararchitects.io/blog/ai-apps-harnesses-for-angular/" rel="noopener noreferrer"&gt;That part of the journey deserves its own post&lt;/a&gt;. Here, I want to stay focused on one question: which LLM is the best fit for &lt;em&gt;Angular&lt;/em&gt; development?&lt;/p&gt;

&lt;p&gt;There is no public &lt;em&gt;Angular&lt;/em&gt; benchmark, so we have to combine two imperfect sources: existing public coding benchmarks and personal experience from real &lt;em&gt;Angular&lt;/em&gt; work. I want to start with the public benchmarks and then move to my own verdict.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR: My Current Ranking
&lt;/h2&gt;

&lt;p&gt;In short: &lt;strong&gt;Opus 4.7&lt;/strong&gt; is my #1 for &lt;em&gt;Angular&lt;/em&gt;, &lt;strong&gt;GPT 5.5&lt;/strong&gt; my #1 all-rounder, &lt;strong&gt;Composer 2.5&lt;/strong&gt; the newcomer to watch, and &lt;strong&gt;Gemini 3.5 Flash&lt;/strong&gt; still experimental for now. The table below has the details:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqj2thh985qu0d2g9an78.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqj2thh985qu0d2g9an78.png" alt="Current LLM ranking for Angular development" width="800" height="310"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Which Frontier LLMs Are Relevant for &lt;em&gt;Angular&lt;/em&gt; in May 2026?
&lt;/h2&gt;

&lt;p&gt;I personally want to use the best and latest LLMs that I can realistically use for &lt;em&gt;Angular&lt;/em&gt; development, so I will mostly talk about frontier models that are generally available through common developer tools, APIs, or IDE integrations.&lt;/p&gt;

&lt;p&gt;In my subjective opinion, the most relevant frontier LLMs for &lt;em&gt;Angular&lt;/em&gt; development currently are (sorted by release date – earliest to latest):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.anthropic.com/news/claude-opus-4-7" rel="noopener noreferrer"&gt;Opus 4.7&lt;/a&gt; by Anthropic, April 16, 2026&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://openai.com/index/introducing-gpt-5-5/" rel="noopener noreferrer"&gt;GPT 5.5&lt;/a&gt; by OpenAI, April 23, 2026&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://cursor.com/changelog/composer-2-5" rel="noopener noreferrer"&gt;Composer 2.5&lt;/a&gt; by Cursor, May 18, 2026&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://blog.google/innovation-and-ai/models-and-research/gemini-models/gemini-3-5/" rel="noopener noreferrer"&gt;Gemini 3.5 Flash&lt;/a&gt; by Google, May 19, 2026&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The last two were released to the public only a few days before I wrote this, so I have not had the chance to experiment with them a lot yet. Still, I think Opus 4.7, GPT 5.5, and Composer 2.5 are the current candidates for the &lt;strong&gt;best LLM for &lt;em&gt;Angular&lt;/em&gt; development&lt;/strong&gt;. Gemini 3.5 Flash is included here because it is new and interesting, but my first impression is that it is not really a challenger to the other three models in this post. I'm still pretty sure Gemini 3.5 Pro will be a serious contender once it is released.&lt;/p&gt;

&lt;h2&gt;
  
  
  Public Benchmarks
&lt;/h2&gt;

&lt;p&gt;At Google I/O in May 2026, Google released a bunch of new AI updates. One of those releases was Google Gemini 3.5 Flash, and the release notes included some benchmarks:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1y52xdrvn802dk1j1rss.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1y52xdrvn802dk1j1rss.gif" alt="Gemini 3.5 Benchmark" width="800" height="599"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I see at least two problems here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;This is vendor-provided marketing material for Gemini 3.5 Flash, so it is not really objective.&lt;/li&gt;
&lt;li&gt;We don't know enough about what the benchmarks are, how they were conducted, and what the results really mean for &lt;em&gt;Angular&lt;/em&gt; development.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So what can we use instead? My favorite benchmarks are done by Artificial Analysis. They are still not &lt;em&gt;Angular&lt;/em&gt;-specific, but they give us a better baseline for comparing model capability, speed, and token usage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Artificial Analysis Benchmarks
&lt;/h3&gt;

&lt;p&gt;I want to show you three views from &lt;a href="https://artificialanalysis.ai/agents/coding-agents" rel="noopener noreferrer"&gt;Artificial Analysis&lt;/a&gt;: the "Coding Agent Index" for overall coding-agent performance, "Execution Time" for active agent runtime, and "Token Usage" for how much context and output the models need to solve tasks. The screenshots below were captured on May 26, 2026, so check the live leaderboard for the latest numbers.&lt;/p&gt;

&lt;p&gt;Strictly speaking, these are coding-agent benchmarks, not pure LLM benchmarks. The &lt;strong&gt;harness matters a lot&lt;/strong&gt;: the same model can behave differently in Claude Code, Cursor, Codex, OpenCode, an IDE integration, or a custom agent setup. That is why I cover harnesses and integrations separately in the &lt;a href="https://www.angulararchitects.io/blog/ai-apps-harnesses-for-angular/" rel="noopener noreferrer"&gt;apps/harnesses post&lt;/a&gt; and the practical setup side in the &lt;a href="https://www.angulararchitects.io/blog/ae-harness-setup-for-angular/" rel="noopener noreferrer"&gt;harness setup post&lt;/a&gt;; here I only use these numbers as a rough signal for model quality.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3g26tnz0i4s9lv4ya7l2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3g26tnz0i4s9lv4ya7l2.png" alt="Coding Agent Index" width="800" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Artificial Analysis Coding Agent Index is a composite score built from SWE-Bench-Pro-Hard-AA, Terminal-Bench v2, and SWE-Atlas-QnA. You can read their &lt;a href="https://artificialanalysis.ai/methodology/coding-agents-benchmarking" rel="noopener noreferrer"&gt;coding-agent benchmark methodology&lt;/a&gt; for the details.&lt;/p&gt;

&lt;p&gt;It is useful for quick comparison, but it should be read alongside the per-eval breakdowns. Two agents with similar index values can still have different strengths across repository tasks, terminal workflows, and rubric-based evaluations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F03n4qrk75odlnh6ti9g2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F03n4qrk75odlnh6ti9g2.png" alt="Execution Time" width="800" height="555"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Mean agent wall time per task. Lower is better.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frwwep5vpf3fpeekw41q2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frwwep5vpf3fpeekw41q2.png" alt="Token Usage" width="800" height="719"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Mean input, cache, and output tokens per task.&lt;/p&gt;

&lt;h3&gt;
  
  
  DeepSWE Benchmark
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftle2v3hp7qa80z2xlkok.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftle2v3hp7qa80z2xlkok.png" alt="Recent DeepSWE benchmark" width="800" height="615"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Update May 31st (including Opus 4.8):&lt;/em&gt; Another recent benchmark that matches my day-to-day impression more closely is &lt;a href="https://deepswe.datacurve.ai/blog" rel="noopener noreferrer"&gt;DeepSWE&lt;/a&gt;. In this leaderboard – a snapshot from May 31, 2026 – GPT 5.5 leads at 70%, followed by Opus 4.8 at 58%, GPT 5.4 at 56%, Opus 4.7 at 54%, and both Opus 4.6 and Gemini 3.5 Flash at 28%. DeepSWE has since moved on to v1.1 with updated numbers, so treat these as the May snapshot and check the &lt;a href="https://deepswe.datacurve.ai/" rel="noopener noreferrer"&gt;live leaderboard&lt;/a&gt; for the latest state. That lines up with my current feeling: GPT 5.5 and Opus 4.8 are &lt;strong&gt;both strong&lt;/strong&gt; for serious coding work, while Gemini 3.5 Flash still feels more experimental for &lt;em&gt;Angular&lt;/em&gt; development.&lt;/p&gt;

&lt;p&gt;What I like about this benchmark is that it focuses more on the kind of work that matters in real agentic workflows: can the agent take a short behavioral prompt, find the right area of the codebase, and implement the change cleanly without me spelling out every file, module, and function? That is exactly the kind of workflow where traditional SWE-Bench results can feel disconnected from daily coding experience.&lt;/p&gt;

&lt;p&gt;The most interesting part is not only the ranking itself, but the size of the gaps. A benchmark that makes a smaller model look almost as capable as a frontier coding model is not very useful for my daily work. DeepSWE feels more realistic because those gaps open up again: the weaker models fall back to where I would expect them from real agentic coding, while GPT 5.5 and Opus stay clearly ahead.&lt;/p&gt;

&lt;p&gt;Composer 2.5 is unfortunately not part of this comparison, probably because it is mostly available through Cursor surfaces and not as a normal standalone model API.&lt;/p&gt;

&lt;h3&gt;
  
  
  What the Benchmarks Suggest
&lt;/h3&gt;

&lt;p&gt;In these benchmark results, &lt;a href="https://blog.google/innovation-and-ai/models-and-research/gemini-models/gemini-3-1-pro/" rel="noopener noreferrer"&gt;Gemini 3.1 Pro&lt;/a&gt; – still the current Pro tier, since Gemini 3.5 Pro has not shipped yet – does not look competitive with the other models. Gemini 3.5 Flash looks more interesting, but the public data is still very fresh and not specific to our framework.&lt;/p&gt;

&lt;p&gt;My personal &lt;em&gt;Angular&lt;/em&gt; impression is similar, but not identical: Gemini 3.5 Flash is sometimes fast and sometimes not, and it does not yet feel like a real challenger to my top three for my work. Gemini 3.5 Pro might become the more serious candidate here, but we don't have enough data yet.&lt;/p&gt;

&lt;p&gt;In this benchmark snapshot, GPT 5.5 wins overall and is more efficient. So let me be upfront about the tension: the benchmarks I trust most currently lean GPT 5.5, yet my own &lt;em&gt;Angular&lt;/em&gt; verdict below still puts Opus on top. That gap between leaderboard and lived experience is exactly why I treat these numbers as signals to weigh, not answers to follow.&lt;/p&gt;

&lt;p&gt;I don't want to look at cost in detail here. That deserves &lt;a href="https://www.angulararchitects.io/blog/ai-costs-for-angular/" rel="noopener noreferrer"&gt;its own follow-up&lt;/a&gt; after the tools and harnesses post, because cost depends heavily on how you access these models. For this post, the central question is still the same: which model gives &lt;em&gt;Angular&lt;/em&gt; developers the best mix of quality, speed, and practical usefulness?&lt;/p&gt;

&lt;p&gt;I also don't want to pretend that this post is a real benchmark. The deeper &lt;em&gt;Angular&lt;/em&gt; evidence behind my preferences comes from my own daily work experience with modernization tasks, refactorings, code reviews, testing workflows, and brownfield scenarios. In our &lt;a href="https://www.angulararchitects.io/en/training/agentic-engineering-von-vibe-coding-zu-professionellen-ki-gestuetzten-workflows/" rel="noopener noreferrer"&gt;&lt;strong&gt;Agentic Engineering Workshop&lt;/strong&gt;&lt;/a&gt;, I will show practical examples of why these models became my current favorites for professional &lt;em&gt;Angular&lt;/em&gt; work.&lt;/p&gt;

&lt;p&gt;Before I move to my personal verdict, there are four side questions worth clearing up because they often come up when comparing current LLMs: Chinese models, European models, local models, and whether any of them are actually open source.&lt;/p&gt;

&lt;h3&gt;
  
  
  But what about the Chinese LLMs?
&lt;/h3&gt;

&lt;p&gt;As of May 2026, Chinese LLMs like &lt;a href="https://api-docs.deepseek.com/updates/" rel="noopener noreferrer"&gt;DeepSeek V4&lt;/a&gt;, &lt;a href="https://qwenlm.github.io/qwen-code-docs/en/blog/weekly-update-2026-04-09/" rel="noopener noreferrer"&gt;Qwen 3.6&lt;/a&gt;, and &lt;a href="https://platform.kimi.ai/docs/guide/kimi-k2-6-quickstart" rel="noopener noreferrer"&gt;Kimi K2.6&lt;/a&gt; are &lt;strong&gt;absolutely worth watching&lt;/strong&gt;, especially because they are pushing very hard on coding quality, open weights, and low cost. But for serious &lt;em&gt;Angular&lt;/em&gt; development, I have not seen enough evidence yet to put them in the same tier as Opus 4.7, GPT 5.5, or Composer 2.5, so for now they are interesting to observe, not models I would bet my enterprise work on.&lt;/p&gt;

&lt;h3&gt;
  
  
  And what about the European LLMs?
&lt;/h3&gt;

&lt;p&gt;As of May 2026, European LLMs are mostly a Mistral story for me, with &lt;a href="https://mistral.ai/news/mistral-3" rel="noopener noreferrer"&gt;Mistral Large 3&lt;/a&gt; as the latest model worth tracking: interesting, improving, and relevant when data residency, EU vendors, or open-weight deployment matter. But for raw coding capability in my &lt;em&gt;Angular&lt;/em&gt; work, I would not rank them with the frontier labs right now, so I would treat them as &lt;strong&gt;strategic options&lt;/strong&gt; rather than serious candidates for the best model.&lt;/p&gt;

&lt;h3&gt;
  
  
  And what about local LLMs?
&lt;/h3&gt;

&lt;p&gt;As of May 2026, local LLMs are no longer just toys, but we have to be precise about what "local" means. Running a model on my MacBook Pro is very different from self-hosting a huge open-weight MoE (Mixture of Experts) model on serious GPU infrastructure.&lt;/p&gt;

&lt;p&gt;For local or self-hosted coding workflows, &lt;a href="https://github.com/QwenLM/Qwen3.6" rel="noopener noreferrer"&gt;Qwen 3.6&lt;/a&gt; is probably the most interesting family to watch right now, especially because the Qwen team explicitly focuses on agentic coding, repository-level reasoning, and local deployment via Transformers, llama.cpp, MLX, vLLM, and SGLang. &lt;a href="https://api-docs.deepseek.com/news/news260424" rel="noopener noreferrer"&gt;DeepSeek V4&lt;/a&gt; is also very relevant in the open-weight space, but the larger variants are more realistic for server-side self-hosting than for a normal developer machine. From Europe, &lt;a href="https://mistral.ai/news/mistral-3" rel="noopener noreferrer"&gt;Mistral 3&lt;/a&gt; is interesting because Mistral Large 3 and the smaller Ministral 3 models are open-weight, with the smaller models explicitly targeting edge and local use cases.&lt;/p&gt;

&lt;p&gt;For my professional &lt;em&gt;Angular&lt;/em&gt; work, I would not put local models in the same tier as my current favorites yet. The gap is not only raw model quality, but also tool use, long-running agent reliability, context handling, and integration quality. Still, local models are &lt;strong&gt;becoming useful&lt;/strong&gt; for privacy- or regulatory-sensitive work, CI helpers, and teams that want more control over where their code goes. I would not ignore them anymore, but I also would not make them my choice for enterprise &lt;em&gt;Angular&lt;/em&gt; development today.&lt;/p&gt;

&lt;h3&gt;
  
  
  Are any of these models open source?
&lt;/h3&gt;

&lt;p&gt;This is where the wording gets a bit tricky, because with LLMs people often say "open source" when they really mean "open weights". Claude, GPT, Gemini, and Composer 2.5 are not open source models in the practical sense, although Composer is based on Moonshot's open Kimi checkpoint. DeepSeek, Qwen, Kimi, and some Mistral models are much closer to that world because their weights are available under permissive or semi-permissive licenses. Still, even there, we usually don't get the full training data, training code, and recipe, so I would call them &lt;strong&gt;open-weight models&lt;/strong&gt; rather than fully open source models.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Subjective Verdict
&lt;/h2&gt;

&lt;p&gt;With that context out of the way, I want to move to my own experience with these LLMs. For me, the real question is not only which model wins a benchmark, but which model fits which part of an &lt;em&gt;Angular&lt;/em&gt; workflow. This part is purely subjective, and I'm open to other opinions and experiences. If you disagree with me, please let me know in the comments on X or Reddit.&lt;/p&gt;

&lt;h3&gt;
  
  
  My #1 for &lt;em&gt;Angular&lt;/em&gt;: Opus 4.7 by Anthropic
&lt;/h3&gt;

&lt;p&gt;My favorite model is Opus 4.7. It is the best LLM for &lt;em&gt;Angular&lt;/em&gt; development in my opinion because it has the &lt;strong&gt;strongest code quality&lt;/strong&gt;, the best overall performance, and still feels fast enough for serious agentic work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqvwq5wfbr4fm1lzbefsc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqvwq5wfbr4fm1lzbefsc.png" alt="Opus 4.7 by Anthropic" width="620" height="330"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is also the most versatile LLM. The big shift for me is that Opus 4.7 no longer feels like a junior helper that needs tiny step-by-step prompts, but more like a &lt;strong&gt;senior partner&lt;/strong&gt; that works best when I give it context, constraints, and good questions.&lt;/p&gt;

&lt;p&gt;I think this model is currently the &lt;strong&gt;most capable&lt;/strong&gt; one for complex enterprise &lt;em&gt;Angular&lt;/em&gt; applications. It's also the most expensive one, especially in Extra and Max Mode, but I think it's worth it for the quality of the results.&lt;/p&gt;

&lt;p&gt;I really like doing agentic work with this model, especially for tasks where I want it to explore trade-offs, challenge assumptions, and come back with a concrete implementation plan. In the follow-up post about tools and harnesses, I show how I use it in practice.&lt;/p&gt;

&lt;h3&gt;
  
  
  My #1 all-rounder: GPT 5.5 by OpenAI
&lt;/h3&gt;

&lt;p&gt;GPT 5.5 is also a great LLM for &lt;em&gt;Angular&lt;/em&gt; development, and &lt;a href="https://openai.com/index/introducing-gpt-5-5/" rel="noopener noreferrer"&gt;OpenAI's introduction&lt;/a&gt; matches what I see in practice: its real strength is &lt;strong&gt;focused implementation work&lt;/strong&gt;. It writes excellent code and is very token-efficient, especially when you do not force the highest reasoning level. However, it is not as versatile as Opus 4.7, and I find it more sensitive to context: if it starts from the wrong assumption, I often get better results by opening a fresh thread with clearer constraints instead of trying to steer the old one back.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feyv5xickpcxbj50kbqtq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feyv5xickpcxbj50kbqtq.png" alt="GPT 5.5 by OpenAI" width="620" height="330"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That efficiency matters because GPT 5.5 is expensive per token, but it often needs &lt;strong&gt;fewer tokens&lt;/strong&gt; to finish a task than competing frontier models. For practical work, I would start with lower or medium reasoning and only move up when the task really needs it.&lt;/p&gt;

&lt;p&gt;For everyday chat use cases, I really like this model. I use it for a variety of tasks, especially when I want to think through a problem by asking a series of focused questions, then start a clean implementation thread once the direction is clear. For example, if I need help fixing the language of this blog post, I will definitely choose GPT 5.5, but I also (currently) prefer it for modernization and refactoring of &lt;em&gt;Angular&lt;/em&gt; projects. Have you tried resolving hard merge conflicts? Oh boy, that's really a relief. Hard to trust though because it's so fast. I think it's kind of a &lt;strong&gt;senior engineer&lt;/strong&gt; while Opus 4.7 is the better &lt;strong&gt;architect&lt;/strong&gt;, but that's for sure subjective.&lt;/p&gt;

&lt;h3&gt;
  
  
  My #1 newcomer: Composer 2.5 by Cursor
&lt;/h3&gt;

&lt;p&gt;Composer 2.5 is a very interesting LLM for &lt;em&gt;Angular&lt;/em&gt; development because it combines good code quality with &lt;strong&gt;very high speed&lt;/strong&gt;. The most interesting claim from the current discussion around it is that Cursor post-trained it on Moonshot's Kimi K2.5 open-weight checkpoint and, on Cursor's own benchmark, got surprisingly close to GPT 5.5 and Opus 4.7 at a much lower cost. The important caveat is that this is still hard to verify independently because Composer is mostly available through Cursor surfaces, not as a normal model API.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faem6bl742agkf1rzj06c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faem6bl742agkf1rzj06c.png" alt="Composer 2.5 by Cursor" width="620" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If costs are important to you and you cannot rely on generous included usage in paid plans such as Claude Code or Codex, Composer 2.5 might be the &lt;strong&gt;best option for your setup&lt;/strong&gt;, but only if Cursor fits your workflow. I cover tools and harnesses in the follow-up post, and then come back to costs and access models in a &lt;a href="https://www.angulararchitects.io/blog/ai-costs-for-angular/" rel="noopener noreferrer"&gt;dedicated follow-up&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Candidate to watch: Gemini 3.5 Flash now, Gemini 3.5 Pro later
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftpjwbffv6ioq20em7h93.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftpjwbffv6ioq20em7h93.png" alt="Gemini 3.5 Flash by Google" width="620" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Gemini 3.5 Flash is interesting, but my first impression is mixed: sometimes it is fast, sometimes it is not, and for &lt;em&gt;Angular&lt;/em&gt; development it does not yet feel like a &lt;strong&gt;real challenger&lt;/strong&gt; to Opus 4.7, GPT 5.5, or Composer 2.5. I still need more hands-on time and better external benchmark data before I can recommend it with confidence. Gemini 3.5 Pro might become the more serious candidate here, especially if it combines stronger coding quality with more consistent speed.&lt;/p&gt;

&lt;h3&gt;
  
  
  But Alex, what about the new model(s)?
&lt;/h3&gt;

&lt;p&gt;Only two days after publishing this post (on May 28, 2026), Anthropic released &lt;a href="https://www.anthropic.com/news/claude-opus-4-8" rel="noopener noreferrer"&gt;Claude Opus 4.8&lt;/a&gt;. So does that mean this post was already outdated two or three days later?&lt;/p&gt;

&lt;p&gt;I don't think so.&lt;/p&gt;

&lt;p&gt;There is probably some improvement from Opus 4.7 to Opus 4.8, and Anthropic's benchmark numbers are impressive. The new model, and the next ones after it, might beat other models such as GPT 5.5 on several benchmarks. But that does not change the main point of this post: benchmark wins are &lt;strong&gt;useful signals, not final answers&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For real &lt;em&gt;Angular&lt;/em&gt; work, the best model is still the one that &lt;strong&gt;fits your workflow best&lt;/strong&gt;. Try the new models when they arrive, but judge them in your own codebase, with your own tasks, your own tools, and your own review standards.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agentic Engineering Workshop
&lt;/h2&gt;

&lt;p&gt;This is also why I don't think about LLMs in isolation anymore: the model, the app, my &lt;em&gt;Angular&lt;/em&gt; Guardrails, my &lt;em&gt;Angular&lt;/em&gt; Coding Style Guide, the &lt;em&gt;Angular&lt;/em&gt; Skills, and the review workflow belong together.&lt;/p&gt;

&lt;p&gt;If you want to choose the right model for your &lt;em&gt;Angular&lt;/em&gt; work – and actually put it to work on real modernization, refactoring, testing, and review tasks – join our &lt;strong&gt;Agentic Engineering Workshop&lt;/strong&gt;, available in English and German.&lt;/p&gt;

&lt;p&gt;In this workshop, advanced &lt;em&gt;Angular&lt;/em&gt; developers learn how to move from vibe coding to traceable Agentic Engineering workflows: AI-ready project setup, guardrails, spec-first and plan-first workflows, UX and component prototyping, code review, testing, and brownfield refactoring.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🤖 &lt;a href="https://www.angulararchitects.io/en/training/agentic-engineering-von-vibe-coding-zu-professionellen-ki-gestuetzten-workflows/" rel="noopener noreferrer"&gt;&lt;strong&gt;Agentic Engineering Workshop&lt;/strong&gt;&lt;/a&gt; – 2 days, remote&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The real shift is not that these models can generate code. The real shift is that they can &lt;strong&gt;participate in the engineering workflow&lt;/strong&gt;: asking questions, clarifying requirements, exploring trade-offs, and helping us move from vague intent to working &lt;em&gt;Angular&lt;/em&gt; code.&lt;/p&gt;

&lt;p&gt;For serious &lt;em&gt;Angular&lt;/em&gt; work, my current recommendation is simple: start with Opus 4.7 when quality, architecture, and complex agentic work matter most. Use GPT 5.5 when you want a very strong all-rounder that is efficient, pleasant to work with, and great for writing, modernization, and focused implementation tasks.&lt;/p&gt;

&lt;p&gt;Watch Composer 2.5 closely, especially if you already work in Cursor and care a lot about speed and cost. Treat Gemini 3.5 Flash as experimental for now, and revisit once Gemini 3.5 Pro ships.&lt;/p&gt;

&lt;p&gt;As the Opus 4.8 update above already shows, this space changes too quickly for fixed rankings to stay perfect for long. So whatever model you choose, don't rely on benchmarks alone: the real test is still whether the model helps you &lt;strong&gt;ship better &lt;em&gt;Angular&lt;/em&gt; code&lt;/strong&gt; in your own codebase.&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://www.angulararchitects.io/blog/ai-apps-harnesses-for-angular/" rel="noopener noreferrer"&gt;next post&lt;/a&gt;, I look at the other half of the story: which apps, IDE integrations, and agentic coding harnesses actually make these models useful in daily &lt;em&gt;Angular&lt;/em&gt; work. After that, I go deeper into &lt;a href="https://www.angulararchitects.io/blog/ai-costs-for-angular/" rel="noopener noreferrer"&gt;AI coding costs&lt;/a&gt;, &lt;a href="https://www.angulararchitects.io/blog/ai-data-privacy-for-angular/" rel="noopener noreferrer"&gt;what AI coding tools do with your code&lt;/a&gt;, and my &lt;a href="https://www.angulararchitects.io/blog/ai-personal-verdict/" rel="noopener noreferrer"&gt;personal agentic coding verdict&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thank you for reading 🙏 this blog post was written by &lt;a href="https://alex.thalhammer.name" rel="noopener noreferrer"&gt;Alexander Thalhammer&lt;/a&gt;. For feedback, remarks or questions, please reach out to me ❤️&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>angular</category>
      <category>llm</category>
    </item>
    <item>
      <title>Why Angular ARIA in v21 is pretty neat</title>
      <dc:creator>Alexander Thalhammer</dc:creator>
      <pubDate>Mon, 01 Dec 2025 21:48:45 +0000</pubDate>
      <link>https://dev.to/lxt/why-angular-aria-in-v21-is-pretty-neat-1652</link>
      <guid>https://dev.to/lxt/why-angular-aria-in-v21-is-pretty-neat-1652</guid>
      <description>&lt;p&gt;&lt;em&gt;Angular ARIA&lt;/em&gt; is a collection of &lt;strong&gt;headless, accessible directives&lt;/strong&gt; that implement common &lt;strong&gt;WAI-ARIA patterns&lt;/strong&gt;. The directives handle &lt;strong&gt;keyboard interactions&lt;/strong&gt;, &lt;a href="https://www.angulararchitects.io/blog/aria-roles-attributes/" rel="noopener noreferrer"&gt;&lt;strong&gt;ARIA attributes&lt;/strong&gt;&lt;/a&gt;, &lt;strong&gt;focus management&lt;/strong&gt;, and &lt;strong&gt;screen reader support&lt;/strong&gt;. All you have to do is provide the &lt;strong&gt;HTML&lt;/strong&gt; structure, &lt;strong&gt;CSS&lt;/strong&gt; styling, and &lt;strong&gt;business logic&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;You feel like you've already read this before? Perfectly possible, because I've just copied and pasted the paragraph from the official &lt;em&gt;&lt;a href="https://angular.dev/guide/aria/overview#what-is-angular-aria" rel="noopener noreferrer"&gt;Angular ARIA docs&lt;/a&gt;&lt;/em&gt;. Why should I reinvent the wheel, right? It perfectly summarizes the concept.&lt;/p&gt;

&lt;p&gt;So the &lt;em&gt;Angular team&lt;/em&gt; just (together with &lt;em&gt;v21&lt;/em&gt; on &lt;em&gt;Nov 19th, 2025&lt;/em&gt;) released a brand-new collection of components – I mean directives – that implement common web patterns while letting you choose your own HTML, styling (CSS, SCSS, or even Tailwind), and business logic (I'd suggest TypeScript).&lt;/p&gt;

&lt;p&gt;This is pretty much the opposite approach of &lt;a href="https://material.angular.dev/" rel="noopener noreferrer"&gt;&lt;em&gt;Angular Material&lt;/em&gt;&lt;/a&gt;, which is an opinionated Plug &amp;amp; Play Design System. Both are built on top of the &lt;a href="https://www.angulararchitects.io/blog/angular-cdk-accessibility/" rel="noopener noreferrer"&gt;&lt;em&gt;Angular CDK&lt;/em&gt; A11y&lt;/a&gt; package. So what do we get here?&lt;/p&gt;

&lt;h2&gt;
  
  
  Components
&lt;/h2&gt;

&lt;p&gt;In the first release with &lt;em&gt;v21&lt;/em&gt;, the following &lt;strong&gt;12 components / directives / UI patterns&lt;/strong&gt; are available:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;search and select&lt;/th&gt;
&lt;th&gt;navigation / menu&lt;/th&gt;
&lt;th&gt;content organization&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://angular.dev/guide/aria/autocomplete" rel="noopener noreferrer"&gt;Autocomplete&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://angular.dev/guide/aria/menu" rel="noopener noreferrer"&gt;Menu&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://angular.dev/guide/aria/accordion" rel="noopener noreferrer"&gt;Accordion&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://angular.dev/guide/aria/listbox" rel="noopener noreferrer"&gt;Listbox&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://angular.dev/guide/aria/menubar" rel="noopener noreferrer"&gt;Menubar&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://angular.dev/guide/aria/tabs" rel="noopener noreferrer"&gt;Tabs&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;a href="https://angular.dev/guide/aria/select" rel="noopener noreferrer"&gt;Select&lt;/a&gt; &amp;amp; &lt;a href="https://angular.dev/guide/aria/multiselect" rel="noopener noreferrer"&gt;Multiselect&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;&lt;a href="https://angular.dev/guide/aria/toolbar" rel="noopener noreferrer"&gt;Toolbar&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://angular.dev/guide/aria/tree" rel="noopener noreferrer"&gt;Tree&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://angular.dev/guide/aria/combobox" rel="noopener noreferrer"&gt;Combobox&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://angular.dev/guide/aria/grid" rel="noopener noreferrer"&gt;Grid&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&amp;nbsp;&lt;br&gt;
There are – of course – plans to extend this in the future 😏&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is this neat?
&lt;/h2&gt;

&lt;p&gt;Using the &lt;em&gt;Angular ARIA&lt;/em&gt; directives is a great way to build &lt;em&gt;Angular apps&lt;/em&gt; by &lt;strong&gt;offloading some of the heavy lifting&lt;/strong&gt; to the &lt;em&gt;Angular team&lt;/em&gt; while still allowing &lt;strong&gt;full customization&lt;/strong&gt; and &lt;strong&gt;user-interface branding&lt;/strong&gt;. BTW: Check out &lt;a href="https://www.youtube.com/watch?v=ihOlHa3I7eI" rel="noopener noreferrer"&gt;Jessica's talk at ViteConf on YT&lt;/a&gt; for other things in &lt;em&gt;NG v21&lt;/em&gt; that are neat.&lt;/p&gt;

&lt;h3&gt;
  
  
  When to use Angular ARIA?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You &lt;strong&gt;know&lt;/strong&gt; how to style things (at least Senior in CSS)&lt;/li&gt;
&lt;li&gt;You build a custom &lt;strong&gt;Design System&lt;/strong&gt; or an &lt;strong&gt;enterprise component library&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;You don't use any Design System and instead handcraft everything yourself&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  When to avoid Angular ARIA?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You &lt;strong&gt;don't know&lt;/strong&gt; how to style (Junior in CSS – join my &lt;a href="https://www.angulararchitects.io/en/training/angular-styling-workshop/" rel="noopener noreferrer"&gt;&lt;strong&gt;NG Styling Workshop&lt;/strong&gt;&lt;/a&gt; 🎨)&lt;/li&gt;
&lt;li&gt;You use a &lt;strong&gt;Design System&lt;/strong&gt; or an &lt;strong&gt;enterprise component library&lt;/strong&gt; (should be covered there)&lt;/li&gt;
&lt;li&gt;You don't care about accessibility (come on – don't be a jerk!)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Angular Styling Best Choices Matrix
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Level&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;
&lt;strong&gt;Angular Material&lt;/strong&gt; 🔌&lt;/th&gt;
&lt;th&gt;
&lt;strong&gt;3rd-party DS&lt;/strong&gt; 🎨&lt;/th&gt;
&lt;th&gt;
&lt;strong&gt;Angular ARIA&lt;/strong&gt; ♿&lt;/th&gt;
&lt;th&gt;
&lt;strong&gt;Custom UI&lt;/strong&gt; 🛠️&lt;/th&gt;
&lt;th&gt;
&lt;strong&gt;Custom DS&lt;/strong&gt; 🏢&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Design&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🎨 Very opinionated&lt;/td&gt;
&lt;td&gt;⚡ Plug &amp;amp; Play&lt;/td&gt;
&lt;td&gt;🤷 Depends on you&lt;/td&gt;
&lt;td&gt;🤷 Depends on you&lt;/td&gt;
&lt;td&gt;🤷 Depends on you&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Accessibility&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;⚡ Plug &amp;amp; Play&lt;/td&gt;
&lt;td&gt;🙂 Usually okay&lt;/td&gt;
&lt;td&gt;⭐ Best Choice&lt;/td&gt;
&lt;td&gt;🤷 Depends on you&lt;/td&gt;
&lt;td&gt;🤷 Depends on you&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Effort&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;⭐ Very low&lt;/td&gt;
&lt;td&gt;🙂 Medium&lt;/td&gt;
&lt;td&gt;😬 High&lt;/td&gt;
&lt;td&gt;😅 Only if 1 app&lt;/td&gt;
&lt;td&gt;💀 Boss will kill ya&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CSS Skills&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🟢 None&lt;/td&gt;
&lt;td&gt;🟡 Junior&lt;/td&gt;
&lt;td&gt;🔴 Senior&lt;/td&gt;
&lt;td&gt;🔴 Senior&lt;/td&gt;
&lt;td&gt;🟣 Master&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Customization&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🔒 Hard&lt;/td&gt;
&lt;td&gt;🤷 Depends&lt;/td&gt;
&lt;td&gt;✔ Included&lt;/td&gt;
&lt;td&gt;♾️ No limits&lt;/td&gt;
&lt;td&gt;⭐ Best Choice&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;NG Updates&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🚫 Don’t customize&lt;/td&gt;
&lt;td&gt;😬 Sometimes painful&lt;/td&gt;
&lt;td&gt;🙂 Smooth&lt;/td&gt;
&lt;td&gt;😎 Very smooth&lt;/td&gt;
&lt;td&gt;😵 A lot of work&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use it for&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🚀 Prototypes &amp;amp; demos&lt;/td&gt;
&lt;td&gt;💸 Low-budget/legacy&lt;/td&gt;
&lt;td&gt;🌱 Greenfield&lt;/td&gt;
&lt;td&gt;🎨 Hobbies&lt;/td&gt;
&lt;td&gt;🏢 Enterprise apps&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&amp;nbsp;&lt;br&gt;
Stay tuned for my next blog post comparing popular &lt;strong&gt;3rd party Design Systems&lt;/strong&gt; for Angular vs. &lt;em&gt;Angular Material&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Workshops
&lt;/h2&gt;

&lt;p&gt;If you want to deep-dive into &lt;em&gt;Angular&lt;/em&gt;, we offer a variety of workshops – in English and German.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.angulararchitects.io/en/training/angular-best-practices/" rel="noopener noreferrer"&gt;&lt;strong&gt;Best Practices Workshop&lt;/strong&gt;&lt;/a&gt; 📈 (including Design Systems &amp;amp; ARIA)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.angulararchitects.io/en/training/angular-styling-workshop/" rel="noopener noreferrer"&gt;&lt;strong&gt;NG Styling Workshop&lt;/strong&gt;&lt;/a&gt; 🎨 (including Design Systems)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.angulararchitects.io/en/training/angular-accessibility-workshop/" rel="noopener noreferrer"&gt;&lt;strong&gt;Accessibility Workshop&lt;/strong&gt;&lt;/a&gt; ♿ (including WAI ARIA)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.angulararchitects.io/en/training/angular-performance-optimization-workshop/" rel="noopener noreferrer"&gt;&lt;strong&gt;Performance Workshop&lt;/strong&gt;&lt;/a&gt; 🚀 (because it's neat)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Angular ARIA&lt;/em&gt; delivers headless, accessible building blocks for fully customizable &lt;em&gt;Angular components&lt;/em&gt;. It shifts complexity – keyboard handling, ARIA attributes, focus management – to the &lt;em&gt;Angular team&lt;/em&gt;, making it ideal for custom Design Systems and tailored UIs without relying on heavy, opinionated frameworks.&lt;/p&gt;

&lt;p&gt;This blog post was written by &lt;a href="https://alex.thalhammer.name" rel="noopener noreferrer"&gt;Alexander Thalhammer&lt;/a&gt;. Comment it &lt;a href="https://www.linkedin.com/posts/thalhammer_angular-aria-initial-version-released-angulararchitects-activity-7401368955434545152-_bms" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, &lt;a href="https://bsky.app/profile/lxt.bsky.social/post/3m6xfgslozs25" rel="noopener noreferrer"&gt;bsky&lt;/a&gt;, &lt;a href="https://x.com/LX_T/status/1995603392560033812" rel="noopener noreferrer"&gt;X&lt;/a&gt; or &lt;a href="https://www.reddit.com/r/angular/comments/1odc1ni/comment/nrrzc6k/" rel="noopener noreferrer"&gt;Reddit&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>angular</category>
      <category>design</category>
    </item>
    <item>
      <title>Enhancing A11y with Angular CDK</title>
      <dc:creator>Alexander Thalhammer</dc:creator>
      <pubDate>Thu, 05 Jun 2025 10:00:00 +0000</pubDate>
      <link>https://dev.to/lxt/enhancing-a11y-with-angular-cdk-9l6</link>
      <guid>https://dev.to/lxt/enhancing-a11y-with-angular-cdk-9l6</guid>
      <description>&lt;p&gt;The &lt;a href="https://material.angular.dev/cdk/a11y/overview" rel="noopener noreferrer"&gt;&lt;strong&gt;@angular/cdk/a11y&lt;/strong&gt;&lt;/a&gt; package is part of the &lt;em&gt;Angular Component Dev Kit (CDK)&lt;/em&gt; and provides a collection of services, directives, SASS mixins, and other utilities to improve the Accessibility (A11y) of your &lt;em&gt;Angular apps&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;These tools are invaluable when building reusable, presentation-focused components with screen-reader announcements, robust keyboard and focus support, high-contrast theming, and more. They’re especially useful if you’re creating an &lt;em&gt;Angular Component Library&lt;/em&gt; or a &lt;em&gt;Design System&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Below are some of my favorites, along with brief code examples.&lt;/p&gt;

&lt;h2&gt;
  
  
  LiveAnnouncer for Screen Readers
&lt;/h2&gt;

&lt;p&gt;The first service we want to highlight is the &lt;strong&gt;&lt;a href="https://material.angular.dev/cdk/a11y/overview#liveannouncer" rel="noopener noreferrer"&gt;LiveAnnouncer&lt;/a&gt;&lt;/strong&gt;. It allows you to announce messages to screen readers, which is particularly useful for providing feedback after user interactions. It's so simple: I often replace my &lt;code&gt;console.log&lt;/code&gt; calls with announcements.&lt;/p&gt;

&lt;p&gt;Here's an example of that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;liveAnnouncer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;LiveAnnouncer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flights&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// console.log('Found ' + this.flights().length + ' flights');&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;liveAnnouncer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;announce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Found &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flights&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; flights&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// console.log('No flights found');&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;liveAnnouncer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;announce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;No flights found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure to test your announcements with the use of a &lt;strong&gt;&lt;a href="https://www.angulararchitects.io/blog/accessibility-testing-tools" rel="noopener noreferrer"&gt;screen reader&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Focus Tools
&lt;/h2&gt;

&lt;p&gt;The &lt;em&gt;Angular CDK&lt;/em&gt; also includes several tools supporting your focus management.&lt;/p&gt;

&lt;h3&gt;
  
  
  Focus Trap
&lt;/h3&gt;

&lt;p&gt;The &lt;em&gt;&lt;strong&gt;&lt;a href="https://material.angular.dev/cdk/a11y/overview#focustrap" rel="noopener noreferrer"&gt;FocusTrap&lt;/a&gt;&lt;/strong&gt; Directive&lt;/em&gt; allows you to trap focus within a specific element, ensuring that keyboard navigation remains within that element until the user explicitly exits it.&lt;/p&gt;

&lt;p&gt;This is particularly useful for modal dialogs or pop-ups:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dialog&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"awesome-dialog"&lt;/span&gt; &lt;span class="na"&gt;cdkTrapFocus&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;!–&lt;/span&gt;&lt;span class="na"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Yay&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;focus&lt;/span&gt; &lt;span class="na"&gt;won&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="na"&gt;t&lt;/span&gt; &lt;span class="na"&gt;leave&lt;/span&gt; &lt;span class="na"&gt;this&lt;/span&gt; &lt;span class="na"&gt;element&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt; &lt;span class="na"&gt;--&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dialog&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want focus to jump in automatically, enable the &lt;strong&gt;&lt;code&gt;cdkTrapFocusAutoCapture&lt;/code&gt;&lt;/strong&gt; flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dialog&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"awesome-dialog"&lt;/span&gt; &lt;span class="na"&gt;cdkTrapFocus&lt;/span&gt; &lt;span class="na"&gt;[cdkTrapFocusAutoCapture]=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;!–&lt;/span&gt;&lt;span class="na"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Yay&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;focus&lt;/span&gt; &lt;span class="na"&gt;won&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="na"&gt;t&lt;/span&gt; &lt;span class="na"&gt;leave&lt;/span&gt; &lt;span class="na"&gt;this&lt;/span&gt; &lt;span class="na"&gt;element&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt; &lt;span class="na"&gt;--&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dialog&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this flag set to &lt;code&gt;true&lt;/code&gt;, the focus will automatically be captured when the dialog opens. However, it may not always be the best choice to have an element automatically capture focus, so we can avoid this with a trick:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dialog&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"awesome-dialog"&lt;/span&gt; &lt;span class="na"&gt;cdkTrapFocus&lt;/span&gt; &lt;span class="na"&gt;[cdkTrapFocusAutoCapture]=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h3&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"awesome-dialog__title"&lt;/span&gt; &lt;span class="na"&gt;tabindex=&lt;/span&gt;&lt;span class="s"&gt;"-1"&lt;/span&gt; &lt;span class="na"&gt;cdkFocusInitial&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;!–&lt;/span&gt;&lt;span class="na"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Yay&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;focus&lt;/span&gt; &lt;span class="na"&gt;won&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="na"&gt;t&lt;/span&gt; &lt;span class="na"&gt;leave&lt;/span&gt; &lt;span class="na"&gt;this&lt;/span&gt; &lt;span class="na"&gt;element&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt; &lt;span class="na"&gt;--&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dialog&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By adding &lt;strong&gt;&lt;code&gt;cdkFocusInitial&lt;/code&gt;&lt;/strong&gt; the title will be secretly focused initially. Since it has tabindex &lt;code&gt;-1&lt;/code&gt;, it won't be focusable by keyboard navigation afterward.&lt;/p&gt;

&lt;h3&gt;
  
  
  Focus Regions
&lt;/h3&gt;

&lt;p&gt;The &lt;em&gt;&lt;strong&gt;&lt;a href="https://material.angular.dev/cdk/a11y/overview#regions" rel="noopener noreferrer"&gt;Regions&lt;/a&gt;&lt;/strong&gt;&lt;/em&gt; are a powerful tool that allows you to define a specific area of your application where focus should be managed. This is particularly useful for complex components like dropdowns or menus, where you want to ensure that focus remains within the component while it is open.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;cdkFocusRegionStart&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;cdkFocusRegionEnd&lt;/code&gt;&lt;/strong&gt; and optionally&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;cdkFocusInitial&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's an example of how to use the &lt;em&gt;&lt;strong&gt;Regions&lt;/strong&gt;&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;nav&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;routerLink&lt;/span&gt; &lt;span class="na"&gt;routerLinkActive=&lt;/span&gt;&lt;span class="s"&gt;"awesome"&lt;/span&gt; &lt;span class="na"&gt;ariaCurrentWhenActive=&lt;/span&gt;&lt;span class="s"&gt;"page"&lt;/span&gt; &lt;span class="na"&gt;cdkFocusRegionStart&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Focus region start&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;routerLink&lt;/span&gt; &lt;span class="na"&gt;routerLinkActive=&lt;/span&gt;&lt;span class="s"&gt;"awesome"&lt;/span&gt; &lt;span class="na"&gt;ariaCurrentWhenActive=&lt;/span&gt;&lt;span class="s"&gt;"page"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Another focusable link&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;routerLink&lt;/span&gt; &lt;span class="na"&gt;routerLinkActive=&lt;/span&gt;&lt;span class="s"&gt;"awesome"&lt;/span&gt; &lt;span class="na"&gt;ariaCurrentWhenActive=&lt;/span&gt;&lt;span class="s"&gt;"page"&lt;/span&gt; &lt;span class="na"&gt;cdkFocusInitial&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Initially focused&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;routerLink&lt;/span&gt; &lt;span class="na"&gt;routerLinkActive=&lt;/span&gt;&lt;span class="s"&gt;"awesome"&lt;/span&gt; &lt;span class="na"&gt;ariaCurrentWhenActive=&lt;/span&gt;&lt;span class="s"&gt;"page"&lt;/span&gt; &lt;span class="na"&gt;cdkFocusRegionEnd&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Focus region end&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: To learn about the &lt;code&gt;ariaCurrentWhenActive&lt;/code&gt; please read my post on &lt;a href="https://www.angulararchitects.io/blog/accessible-angular-routes/" rel="noopener noreferrer"&gt;&lt;em&gt;Accessible Angular Routes&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Focus Monitor
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;FocusMonitor&lt;/strong&gt; is a service that allows you to monitor focus changes within your application. It can be used to track when an element gains or loses focus, which is particularly useful for debugging focus-related issues.&lt;/p&gt;

&lt;p&gt;This service can be injected into your components or services, and you can subscribe to focus changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;DestroyRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;effect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ElementRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;viewChild&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;FocusMonitor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;FocusOrigin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/cdk/a11y&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-navbar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;nav #observed class="awesome-nav-cnt"&amp;gt;&amp;lt;!-- children --&amp;gt;&amp;lt;/nav&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AwesomeFocusMonitorComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;destroyRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;DestroyRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;focusMonitor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;FocusMonitor&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;observedElementRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;viewChild&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;required&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ElementRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;observed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;effect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;observedElementRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observedElementRef&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// effect will run when the view is initialized&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;focusMonitor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;monitor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;observedElementRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FocusOrigin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;destroyRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onDestroy&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;focusMonitor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stopMonitoring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;observedElementRef&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is particularly useful for debugging focus-related issues, as it allows you to see when an element gains or loses focus.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;FocusOrigin&lt;/code&gt; can be one of the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;'mouse'&lt;/code&gt; the element was focused with the mouse&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;'keyboard'&lt;/code&gt; it was focused with the keyboard&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;'touch'&lt;/code&gt; it was focused by touching on a touchscreen&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;'program'&lt;/code&gt; it was focused programmatically, whereas&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;null&lt;/code&gt; indicates the element was blurred&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, this is also useful to check if a touch device is being used 😏&lt;/p&gt;

&lt;h2&gt;
  
  
  Styling utilities
&lt;/h2&gt;

&lt;p&gt;The &lt;em&gt;Angular A11y package&lt;/em&gt; also includes two useful Sass mixins.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hidden Elements
&lt;/h3&gt;

&lt;p&gt;Screen readers and other assistive technology skip elements that have &lt;code&gt;display: none&lt;/code&gt;, &lt;code&gt;visibility: hidden&lt;/code&gt;, &lt;code&gt;opacity: 0&lt;/code&gt;, &lt;code&gt;height: 0&lt;/code&gt;, or &lt;code&gt;width: 0&lt;/code&gt;. In some cases, you may need to visually hide an element while &lt;strong&gt;keeping it available to assistive technology&lt;/strong&gt; (e.g. Screen Readers).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@use&lt;/span&gt; &lt;span class="s1"&gt;'@angular/cdk'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;cdk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;a11y-visually-hidden&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"awesome-toggle"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"cdk-visually-hidden"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  High Contrast Mode
&lt;/h3&gt;

&lt;p&gt;Some operating systems and/or devices include a &lt;strong&gt;High Contrast Mode&lt;/strong&gt;. The &lt;em&gt;Angular A11y package&lt;/em&gt; provides a Sass mixin that lets you define styles that only apply in high contrast mode. To create a high contrast style, just wrap it into the high-contrast mixin.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@use&lt;/span&gt; &lt;span class="s1"&gt;'@angular/cdk'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;cdk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;high-contrast&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;outline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="no"&gt;gold&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The mixin works by targeting the forced-colors media query.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessibility Workshop
&lt;/h2&gt;

&lt;p&gt;For those looking to deepen their &lt;em&gt;Angular&lt;/em&gt; expertise, we offer a range of workshops – both in English and German:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;♿ &lt;a href="https://www.angulararchitects.io/en/training/angular-accessibility-workshop/" rel="noopener noreferrer"&gt;&lt;strong&gt;Accessibility Workshop&lt;/strong&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📈 &lt;a href="https://www.angulararchitects.io/en/training/angular-best-practices/" rel="noopener noreferrer"&gt;&lt;strong&gt;Best Practices Workshop&lt;/strong&gt;&lt;/a&gt; (including accessibility-related topics)&lt;/li&gt;
&lt;li&gt;🚀 &lt;a href="https://www.angulararchitects.io/en/training/angular-performance-optimization-workshop/" rel="noopener noreferrer"&gt;&lt;strong&gt;Performance Workshop&lt;/strong&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;With these tools, you can significantly enhance the accessibility of your &lt;em&gt;Angular Apps&lt;/em&gt;. They help ensure that your components are not only functional but also user-friendly for everyone, including those with disabilities.&lt;/p&gt;

&lt;p&gt;One last thing: In this Code lab by Google, you can find some exercises on the most important &lt;em&gt;Angular CDK A11y&lt;/em&gt; tools &lt;a href="https://codelabs.developers.google.com/angular-a11y#8" rel="noopener noreferrer"&gt;https://codelabs.developers.google.com/angular-a11y#8&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  That's all folks!
&lt;/h3&gt;

&lt;p&gt;This is the last edition of our A11y series. I hope you found it helpful and learned something new about making your &lt;em&gt;Angular Apps&lt;/em&gt; more accessible and user-friendly for all of us, and that you are ready for the &lt;strong&gt;European Accessibility Act (EAA)&lt;/strong&gt;. If uncertain, please go back to the start of the &lt;strong&gt;&lt;a href="https://www.angulararchitects.io/blog/web-accessibility-in-angular/" rel="noopener noreferrer"&gt;A11y blog series&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This blog post was written by &lt;a href="https://alex.thalhammer.name" rel="noopener noreferrer"&gt;Alexander Thalhammer&lt;/a&gt;. Follow me on &lt;a href="https://at.linkedin.com/in/thalhammer" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt;, &lt;a href="https://twitter.com/LX_T" rel="noopener noreferrer"&gt;X&lt;/a&gt; or &lt;a href="https://github.com/L-X-T" rel="noopener noreferrer"&gt;giThub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://material.angular.dev/cdk/a11y/overview" rel="noopener noreferrer"&gt;Angular CDK Accessibility – official docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://codelabs.developers.google.com/angular-a11y#8" rel="noopener noreferrer"&gt;Google codelab on A11y&lt;/a&gt; by Emma Twersky&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>angular</category>
      <category>a11y</category>
    </item>
    <item>
      <title>Building Accessible Forms with Angular</title>
      <dc:creator>Alexander Thalhammer</dc:creator>
      <pubDate>Mon, 02 Jun 2025 10:00:00 +0000</pubDate>
      <link>https://dev.to/lxt/building-accessible-forms-with-angular-38ii</link>
      <guid>https://dev.to/lxt/building-accessible-forms-with-angular-38ii</guid>
      <description>&lt;p&gt;Accessible &lt;em&gt;Angular Forms&lt;/em&gt; are essential to ensure that all our users – including those with disabilities – can interact with our &lt;em&gt;Angular App&lt;/em&gt; effectively. By implementing forms with Accessibility (A11y) in mind, we meet legal (&lt;a href="https://www.angulararchitects.io/blog/web-accessibility-in-angular/" rel="noopener noreferrer"&gt;EAA 2025&lt;/a&gt; ♿) and ethical standards, and create a more inclusive experience. Accessible forms work better with screen readers, keyboard navigation, and assistive technologies – which not only helps users with impairments but also &lt;strong&gt;enhances the overall UX&lt;/strong&gt; of our &lt;em&gt;Angular Apps&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Angular Forms
&lt;/h2&gt;

&lt;p&gt;In &lt;em&gt;Angular,&lt;/em&gt; we have two types of forms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Template-driven Forms&lt;/strong&gt;: These are simpler and more declarative, relying on &lt;em&gt;Angular Directives&lt;/em&gt; to create forms. They are built around the &lt;code&gt;NgModel&lt;/code&gt; directive and generally used for simple forms. As the name suggests, these are implemented in the template through attributes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reactive Forms&lt;/strong&gt;: These are more powerful and flexible, allowing for complex form structures and dynamic validation. They are built in the component class around the &lt;code&gt;FormGroup&lt;/code&gt; and &lt;code&gt;FormControl&lt;/code&gt; classes. Most often the &lt;code&gt;FormBuilder&lt;/code&gt; service is used to create the forms.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In 2025, the &lt;em&gt;Angular team&lt;/em&gt; is expected to work on &lt;strong&gt;&lt;a href="https://angular.dev/roadmap#improving-the-angular-developer-experience" rel="noopener noreferrer"&gt;Signal integration&lt;/a&gt;&lt;/strong&gt; for &lt;em&gt;Angular Forms&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;However, no matter which type of form you choose – and whether you migrate from Observables to Signals or not – 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keyboard Navigation &amp;amp; Tab Focus
&lt;/h2&gt;

&lt;p&gt;Keyboard navigation is a critical part of form A11y. Many users rely on the keyboard – rather than a mouse – to move through a form using the Tab, Shift + Tab, Arrow, and Enter keys. Ensuring a logical tab order, using semantic HTML elements like &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;, and avoiding custom controls that break default behavior, helps users navigate efficiently. You should not change the order. However, you can add &lt;code&gt;tabindex="0"&lt;/code&gt; to include non-interactive elements or custom components, and use &lt;code&gt;tabindex="-1"&lt;/code&gt; to remove elements from the tab order.&lt;/p&gt;

&lt;p&gt;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 😎&lt;/p&gt;

&lt;h2&gt;
  
  
  Form Fields
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Labels &amp;amp; type attribute
&lt;/h3&gt;

&lt;p&gt;To ensure A11y, always associate &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; elements with their corresponding form controls using the for and id attributes. This improves support for screen readers and enables better keyboard navigation – 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 &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; elements to ensure proper behavior, like submitting a form when pressing Enter on a &lt;code&gt;&amp;lt;button type="submit"&amp;gt;&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"from"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;From&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"from"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"from"&lt;/span&gt; &lt;span class="err"&gt;[...]&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Grouping fields
&lt;/h3&gt;

&lt;p&gt;When we have a group of related inputs, especially radio buttons or checkboxes, screen readers benefit from extra semantic context by &lt;code&gt;&amp;lt;fieldset&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;legend&amp;gt;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;fieldset&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;legend&amp;gt;&lt;/span&gt;Flight Class&lt;span class="nt"&gt;&amp;lt;/legend&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"economy"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Economy&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"class"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"economy"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"economy"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"business"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Business&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"class"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"business"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"business"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/fieldset&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This provides context for assistive tech. Without this, users may hear "Economy" and "Business" without understanding they are part of a group. The &lt;code&gt;&amp;lt;fieldset&amp;gt;&lt;/code&gt; element groups related controls, while the &lt;code&gt;&amp;lt;legend&amp;gt;&lt;/code&gt; provides a caption for the group.&lt;/p&gt;

&lt;h3&gt;
  
  
  Required fields
&lt;/h3&gt;

&lt;p&gt;When a form element (like &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt;, or &lt;code&gt;&amp;lt;textarea&amp;gt;&lt;/code&gt;) must have a value, use the &lt;code&gt;required&lt;/code&gt; 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 &lt;code&gt;required&lt;/code&gt; attribute to both input fields in your form, and consider adding an asterisk (*) as an additional visual indicator.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Name (*)&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Autocomplete
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;autocomplete&lt;/code&gt; 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 &lt;code&gt;autocomplete&lt;/code&gt; attribute, you can specify the type of data expected in each field, such as &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;email&lt;/code&gt;, or &lt;code&gt;address&lt;/code&gt;. This not only enhances usability but also helps with A11y by providing clear context for screen readers and assistive technologies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"phone"&lt;/span&gt; &lt;span class="na"&gt;autocomplete=&lt;/span&gt;&lt;span class="s"&gt;"phone"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To avoid autocomplete, we should set the &lt;code&gt;autocomplete&lt;/code&gt; attribute to &lt;code&gt;off&lt;/code&gt;. This is especially useful for sensitive information like passwords or when you want to ensure that users enter fresh data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;autocomplete=&lt;/span&gt;&lt;span class="s"&gt;"off"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ARIA attributes
&lt;/h3&gt;

&lt;p&gt;ARIA attributes (more on them in our &lt;a href="https://www.angulararchitects.io/blog/aria-roles-attributes/" rel="noopener noreferrer"&gt;last post&lt;/a&gt;) can enhance the A11y of our &lt;em&gt;Angular Forms&lt;/em&gt; by providing additional context to assistive technologies when native HTML alone isn’t enough. Attributes like &lt;code&gt;aria-label&lt;/code&gt; or &lt;code&gt;aria-labelledby&lt;/code&gt; can offer accessible names for form controls when visible labels aren’t practical. While ARIA should never replace semantic HTML, it’s a powerful tool to bridge A11y gaps and ensure all users can understand and interact with your forms effectively.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"search"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"search"&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Search flights"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Search..."&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For validation, &lt;code&gt;aria-invalid="true"&lt;/code&gt; can indicate a validation error and &lt;code&gt;aria-describedby&lt;/code&gt; can provide additional context or instructions. For example, if a user enters an invalid email address, you can set &lt;code&gt;aria-invalid="true"&lt;/code&gt; on the input field and use &lt;code&gt;aria-describedby&lt;/code&gt; to point to an error message that explains the issue.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;@let hasFromErrors = flightSearchForm.controls['from'] &lt;span class="err"&gt;&amp;amp;&amp;amp;&lt;/span&gt; flightSearchForm.controls['from'].touched &lt;span class="err"&gt;&amp;amp;&amp;amp;&lt;/span&gt; flightSearchForm.controls['from'].errors;

&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"from"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"from"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="na"&gt;[attr.aria-invalid]=&lt;/span&gt;&lt;span class="s"&gt;"!!hasFromErrors"&lt;/span&gt; &lt;span class="na"&gt;[attr.aria-describedby]=&lt;/span&gt;&lt;span class="s"&gt;"hasFromErrors ? 'from_error' : null"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Speaking about error messages, let's take a look at how to handle them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Error messages
&lt;/h2&gt;

&lt;p&gt;Accessible error messages help all users understand and correct form issues. Use &lt;code&gt;aria-describedby&lt;/code&gt; (as in the example above) to link inputs to their error messages, and add &lt;code&gt;aria-live="polite"&lt;/code&gt; to ensure screen readers announce them when they appear. Messages should be clear, concise, and not rely on color alone – always provide text or icons for better clarity.&lt;/p&gt;

&lt;p&gt;Personal preferences of &lt;strong&gt;error messages&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;only &lt;strong&gt;after user interaction&lt;/strong&gt; with the form: on blur ("touched" in &lt;em&gt;Angular&lt;/em&gt;) or after submitting.&lt;/li&gt;
&lt;li&gt;focus on the &lt;strong&gt;first invalid control&lt;/strong&gt; (see code example below) upon submitting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;inline&lt;/strong&gt; with the form fields, not at the top or bottom of the form.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;after&lt;/strong&gt; the form field, not before.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;in (dark) red&lt;/strong&gt; and with an icon (e.g., ❌) to make them more visible.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FlightSearchComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;DOCUMENT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// for the focus&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;flightSearchForm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;viewChild&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;required&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;NgForm&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;flightSearchForm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nf"&gt;onSearch&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flightSearchForm&lt;/span&gt;&lt;span class="p"&gt;()?.&lt;/span&gt;&lt;span class="nx"&gt;invalid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;markFormGroupTouched&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flightSearchForm&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;focusFirstInvalidControl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flightSearchForm&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// do the search&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;markFormGroupTouched&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formGroup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FormGroup&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;controls&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;control&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;formGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;control&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;FormGroup&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;markFormGroupTouched&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;control&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;control&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;markAsTouched&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;focusFirstInvalidControl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formGroup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FormGroup&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;controls&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;control&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;formGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;control&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;invalid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;invalidControl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[name="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"]`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;invalidControl&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nf"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;#flightSearchForm&lt;/span&gt;&lt;span class="err"&gt;="&lt;/span&gt;&lt;span class="na"&gt;ngForm&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"fromAirport"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;From (*)&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;

  @let hasFromErrors = flightSearchForm.controls['from'] &lt;span class="err"&gt;&amp;amp;&amp;amp;&lt;/span&gt; flightSearchForm.controls['from'].touched &lt;span class="err"&gt;&amp;amp;&amp;amp;&lt;/span&gt; flightSearchForm.controls['from'].errors;

  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt;
    &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;
    &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"from"&lt;/span&gt;
    &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"fromAirport"&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;
    &lt;span class="na"&gt;[minlength]=&lt;/span&gt;&lt;span class="s"&gt;"minLength"&lt;/span&gt;
    &lt;span class="na"&gt;[maxlength]=&lt;/span&gt;&lt;span class="s"&gt;"maxLength"&lt;/span&gt;
    &lt;span class="na"&gt;[pattern]=&lt;/span&gt;&lt;span class="s"&gt;"pattern"&lt;/span&gt;
    &lt;span class="na"&gt;[attr.aria-invalid]=&lt;/span&gt;&lt;span class="s"&gt;"!!hasFromErrors"&lt;/span&gt;
    &lt;span class="na"&gt;[attr.aria-describedby]=&lt;/span&gt;&lt;span class="s"&gt;"hasFromErrors ? 'fromErrors' : null"&lt;/span&gt;
    &lt;span class="na"&gt;[(ngModel)]=&lt;/span&gt;&lt;span class="s"&gt;"from"&lt;/span&gt;
  &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

  @if (hasFromErrors) {
  &lt;span class="nt"&gt;&amp;lt;app-flight-validation-errors&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"fromErrors"&lt;/span&gt; &lt;span class="na"&gt;[errors]=&lt;/span&gt;&lt;span class="s"&gt;"flightSearchForm.controls['from'].errors"&lt;/span&gt; &lt;span class="na"&gt;fieldLabel=&lt;/span&gt;&lt;span class="s"&gt;"From"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  }
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Accessibility Workshop
&lt;/h2&gt;

&lt;p&gt;For those looking to deepen their &lt;em&gt;Angular&lt;/em&gt; expertise, we offer a range of workshops – both in English and German:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;♿ &lt;a href="https://www.angulararchitects.io/en/training/angular-accessibility-workshop/" rel="noopener noreferrer"&gt;&lt;strong&gt;Accessibility Workshop&lt;/strong&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📈 &lt;a href="https://www.angulararchitects.io/en/training/angular-best-practices/" rel="noopener noreferrer"&gt;&lt;strong&gt;Best Practices Workshop&lt;/strong&gt;&lt;/a&gt; (including accessibility-related topics)&lt;/li&gt;
&lt;li&gt;🚀 &lt;a href="https://www.angulararchitects.io/en/training/angular-performance-optimization-workshop/" rel="noopener noreferrer"&gt;&lt;strong&gt;Performance Workshop&lt;/strong&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building accessible forms in &lt;em&gt;Angular&lt;/em&gt; is not just a best practice – it’s 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 &lt;em&gt;Angular&lt;/em&gt; and &lt;em&gt;Accessibility&lt;/em&gt;, check out my &lt;strong&gt;&lt;a href="https://www.angulararchitects.io/blog/web-accessibility-in-angular/" rel="noopener noreferrer"&gt;A11y blog series&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This blog post was written by &lt;a href="https://alex.thalhammer.name" rel="noopener noreferrer"&gt;Alexander Thalhammer&lt;/a&gt;. Follow me on &lt;a href="https://at.linkedin.com/in/thalhammer" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt;, &lt;a href="https://twitter.com/LX_T" rel="noopener noreferrer"&gt;X&lt;/a&gt; or &lt;a href="https://github.com/L-X-T" rel="noopener noreferrer"&gt;giThub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>a11y</category>
    </item>
    <item>
      <title>ARIA roles and attributes in Angular</title>
      <dc:creator>Alexander Thalhammer</dc:creator>
      <pubDate>Fri, 30 May 2025 13:34:52 +0000</pubDate>
      <link>https://dev.to/lxt/aria-roles-and-attributes-in-angular-3ghl</link>
      <guid>https://dev.to/lxt/aria-roles-and-attributes-in-angular-3ghl</guid>
      <description>&lt;p&gt;ARIA (short for Accessible Rich Internet Applications) roles and attributes are used to improve Accessibility of our &lt;em&gt;Angular apps&lt;/em&gt; – especially for users who rely on assistive technologies like screen readers, voice control, or alternative input devices. Using ARIA is essential for building inclusive, user-friendly web apps.&lt;/p&gt;

&lt;h2&gt;
  
  
  About ARIA
&lt;/h2&gt;

&lt;p&gt;ARIA was developed by the World Wide Web Consortium’s &lt;strong&gt;&lt;a href="https://www.w3.org/WAI/" rel="noopener noreferrer"&gt;Web Accessibility Initiative (WAI)&lt;/a&gt;&lt;/strong&gt; to enhance the accessibility of dynamic web content. The WAI also is the organization behind the &lt;strong&gt;Web Content Accessibility Guidelines (WCAG)&lt;/strong&gt;, which provide a comprehensive framework for A11y – more &lt;a href="https://www.angulararchitects.io/blog/web-accessibility-in-angular/" rel="noopener noreferrer"&gt;about &lt;strong&gt;WCAG&lt;/strong&gt; in the intro of this A11y blog series&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4g52l0ms6vsndc0ty9rg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4g52l0ms6vsndc0ty9rg.png" alt="WAI" width="284" height="70"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Originating in the early 2000s, ARIA was created to bridge the gaps in native HTML, ensuring that modern, interactive and single-page applications are usable by people with disabilities  ♿&lt;/p&gt;

&lt;h2&gt;
  
  
  ARIA in Angular
&lt;/h2&gt;

&lt;p&gt;In &lt;em&gt;Angular&lt;/em&gt;, ARIA roles and attributes can be easily integrated into your components. You can use them directly in your HTML view templates, just like any other HTML attribute. &lt;em&gt;Angular&lt;/em&gt; also provides &lt;strong&gt;built-in support for ARIA&lt;/strong&gt; through directives and bindings, making it easier to manage ARIA properties dynamically.&lt;/p&gt;

&lt;p&gt;When using &lt;strong&gt;static values&lt;/strong&gt;, you can simply add them to your HTML elements or components:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Static ARIA attributes require no extra --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Close"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;X&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, when you want to bind &lt;strong&gt;ARIA attributes dynamically&lt;/strong&gt;, you should use &lt;em&gt;Angular's&lt;/em&gt; property binding syntax ("[]" square brackets) including the "attr." prefix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Dynamic ARIA attribute property binding with "attr." prefix --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;[attr.aria-label]=&lt;/span&gt;&lt;span class="s"&gt;"myActionLabel"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;…&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  ARIA vs. Semantic HTML
&lt;/h2&gt;

&lt;p&gt;While ARIA is a powerful tool for enhancing accessibility, it should be used as a &lt;strong&gt;last resort&lt;/strong&gt;. Native &lt;strong&gt;semantic HTML&lt;/strong&gt; elements and attributes are preferred whenever possible, as they provide built-in accessibility features that ARIA does not have to replicate. Always prefer native elements like &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;header&amp;gt;&lt;/code&gt; over their ARIA counterparts. For example, instead of using &lt;code&gt;role="button"&lt;/code&gt; on a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;, use a &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; element.&lt;/p&gt;

&lt;h2&gt;
  
  
  ARIA Roles
&lt;/h2&gt;

&lt;p&gt;Speaking about ARIA roles, they define what an HTML element is or how it should behave – when native HTML elements are not applicable. They tell assistive technologies how to interpret an element and its purpose within the page. Roles are particularly useful for custom components that don’t have native semantic meaning.&lt;/p&gt;

&lt;h3&gt;
  
  
  Most commonly used ARIA roles
&lt;/h3&gt;

&lt;p&gt;Here is a not exhaustive list of some ot the most commonly used ARIA roles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;role="button"&lt;/code&gt; (prefer &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;) indicates an interactive element that triggers an action.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;role="main"&lt;/code&gt; (prefer &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt;) the main content of your app – typically &lt;code&gt;&amp;lt;router-outlet /&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;role="complementary"&lt;/code&gt; (prefer &lt;code&gt;&amp;lt;aside&amp;gt;&lt;/code&gt;) denotes content that complements – like a sidebar.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;role="navigation"&lt;/code&gt; (prefer &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt;) denotes a section of navigation links – like your route nav.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;role="alert"&lt;/code&gt; used for important messages that should be immediately announced.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;role="dialog"&lt;/code&gt; signifies a modal or popup window.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;role="listbox"&lt;/code&gt; used for a widget that allows the user to select from a list of options.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;role="tablist"&lt;/code&gt; composes a tabbed interface.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;role="tab"&lt;/code&gt; represents a selectable tab in a tabbed interface.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;role="tabpanel"&lt;/code&gt; the container for the content associated with a tab.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Find a comprehensive list of all &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles" rel="noopener noreferrer"&gt;ARIA roles&lt;/a&gt; by MDN.&lt;/p&gt;

&lt;h3&gt;
  
  
  Examples
&lt;/h3&gt;

&lt;p&gt;This could be a &lt;code&gt;&amp;lt;app-dialog&amp;gt;&lt;/code&gt; component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;app-dialog&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"dialog"&lt;/span&gt; &lt;span class="na"&gt;aria-labelledby=&lt;/span&gt;&lt;span class="s"&gt;"dialogTitle"&lt;/span&gt; &lt;span class="na"&gt;aria-modal=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="na"&gt;cdkFocusTrap&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"dialogTitle"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{ dialogTitle() }}&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;{{ dialogContent() }}&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;(click)=&lt;/span&gt;&lt;span class="s"&gt;"onClose()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Close&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/app-dialog&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this simple example, the &lt;code&gt;role="dialog"&lt;/code&gt; indicates that this is a dialog window. The &lt;code&gt;aria-labelledby&lt;/code&gt; attribute associates the dialog with its title, and &lt;code&gt;aria-modal="true"&lt;/code&gt; indicates that the dialog is modal, meaning it prevents interaction with the rest of the page until closed. Note that we also use the &lt;code&gt;cdkFocusTrap&lt;/code&gt; directive to ensure that (keyboard navigation) focus is trapped within the dialog while it is open.&lt;/p&gt;

&lt;p&gt;Another example could be a &lt;strong&gt;tab interface&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;app-tablist&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"tablist"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  @for (tab of tabs(); track tab.id) {
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"tab"&lt;/span&gt; &lt;span class="na"&gt;aria-controls=&lt;/span&gt;&lt;span class="s"&gt;"panel_{{ tab.id }}"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"tab_{{ tab.id }}"&lt;/span&gt;
            &lt;span class="na"&gt;[attr.aria-selected]=&lt;/span&gt;&lt;span class="s"&gt;"activeTab() === tab.id ? 'true' : 'false'"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      {{ tab.label }}
    &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  } @empty {
    No tabs available.
  }
&lt;span class="nt"&gt;&amp;lt;/app-tablist&amp;gt;&lt;/span&gt;
@for (tab of tabs(); track tab.id) {
  &lt;span class="nt"&gt;&amp;lt;app-tab&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"tabpanel"&lt;/span&gt; &lt;span class="na"&gt;aria-labelledby=&lt;/span&gt;&lt;span class="s"&gt;"tab_{{ tab.id }}"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"panel_{{ tab.id }}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    {{ tab.content }}
  &lt;span class="nt"&gt;&amp;lt;/app-tab&amp;gt;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we have a tabbed interface. The &lt;code&gt;role="tablist"&lt;/code&gt; indicates that this is a list of tabs. Each tab has the &lt;code&gt;role="tab"&lt;/code&gt; and is associated with its corresponding panel using &lt;code&gt;aria-controls&lt;/code&gt;. The &lt;code&gt;aria-selected&lt;/code&gt; attribute indicates which tab is currently selected. The panels have the &lt;code&gt;role="tabpanel"&lt;/code&gt; and are associated with their respective tabs using &lt;code&gt;aria-labelledby&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  ARIA Attributes
&lt;/h2&gt;

&lt;p&gt;ARIA attributes provide additional details about an HTML element’s state, properties or relationships. They enhance the &lt;strong&gt;semantic meaning&lt;/strong&gt; of your &lt;em&gt;Angular&lt;/em&gt; components and other HTML elements, especially when default HTML does not fully describe an element’s behavior.&lt;/p&gt;

&lt;p&gt;We distinguish between states and properties. States are dynamic and can change over time, while properties are static and describe the element's characteristics.&lt;/p&gt;

&lt;h3&gt;
  
  
  Most commonly used ARIA attributes
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Widget attributes (states and properties)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;aria-disabled&lt;/code&gt; (state/property): indicates whether an element is disabled or not – not necessary if native &lt;code&gt;disabled&lt;/code&gt; is used.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;aria-required&lt;/code&gt; (property): indicates that user input is required before submitting a form – not necessary if native &lt;code&gt;required&lt;/code&gt; is used.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;aria-expanded&lt;/code&gt; (state): communicates whether an element, such as a collapsible menu, is expanded or collapsed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;aria-hidden&lt;/code&gt; (state): indicates whether an element should be exposed to assistive technologies.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;aria-invalid&lt;/code&gt; (state): indicates whether the value of an input field is valid or invalid.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Live region attributes (states)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;aria-live&lt;/code&gt;: specifies how updates to content should be announced to the user (e.g., &lt;code&gt;assertive&lt;/code&gt; for error messages).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;aria-busy&lt;/code&gt;: indicates whether an element is currently being updated (e.g., loading spinner).&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Drag-and-Drop attributes (states)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;aria-grabbed&lt;/code&gt;: indicates whether an element is currently being dragged (e.g., &lt;code&gt;true&lt;/code&gt; when dragging).&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Relationship attributes (properties)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;aria-label&lt;/code&gt;: provides a text alternative for elements that may not have visible text.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;aria-labelledby&lt;/code&gt;: identifies an element (or elements) that labels the current element.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;aria-describedby&lt;/code&gt;: identifies an element (or elements) that describes the current element.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;aria-controls&lt;/code&gt; (state): references the element(s) whose content is controlled by the current element.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And here is again the full list of all &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes" rel="noopener noreferrer"&gt;ARIA attributes&lt;/a&gt; by MDN.&lt;/p&gt;

&lt;h3&gt;
  
  
  Examples
&lt;/h3&gt;

&lt;p&gt;A toggle button using &lt;code&gt;aria-label&lt;/code&gt; and &lt;code&gt;aria-expanded&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt;
  &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt;
  &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Toggle navigation menu"&lt;/span&gt;
  &lt;span class="na"&gt;[attr.aria-expanded]=&lt;/span&gt;&lt;span class="s"&gt;"isMenuOpen() ? 'true' : 'false'"&lt;/span&gt;
  &lt;span class="na"&gt;(click)=&lt;/span&gt;&lt;span class="s"&gt;"onToggleNav()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"icon-menu"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this simple example, the &lt;code&gt;aria-label&lt;/code&gt; attribute provides a text alternative for the button, indicating its purpose. The &lt;code&gt;aria-expanded&lt;/code&gt; attribute indicates whether the navigation menu is currently open or closed. This is important for screen reader users, as it helps them understand the state of the button.&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;aria-live&lt;/code&gt; combined with &lt;code&gt;role="alert"&lt;/code&gt; for an error message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;@let showFromErrors =
  flightSearchForm.controls.from.errors &lt;span class="err"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
  flightSearchForm.controls.from.touched;

&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt;
  &lt;span class="err"&gt;[...]&lt;/span&gt;
  &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"fromAirport"&lt;/span&gt;
  &lt;span class="na"&gt;[attr.aria-invalid]=&lt;/span&gt;&lt;span class="s"&gt;"!!showFromErrors"&lt;/span&gt;
  &lt;span class="na"&gt;[attr.aria-describedby]=&lt;/span&gt;&lt;span class="s"&gt;"showFromErrors ? 'fromAirportErrors' : null"&lt;/span&gt;
&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
@if (showFromErrors) {
  &lt;span class="nt"&gt;&amp;lt;app-flight-validation-errors&lt;/span&gt;
    &lt;span class="na"&gt;aria-live=&lt;/span&gt;&lt;span class="s"&gt;"assertive"&lt;/span&gt;
    &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"alert"&lt;/span&gt;
    &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"fromAirportErrors"&lt;/span&gt;
    &lt;span class="na"&gt;fieldLabel=&lt;/span&gt;&lt;span class="s"&gt;"From"&lt;/span&gt;
    &lt;span class="na"&gt;[errors]=&lt;/span&gt;&lt;span class="s"&gt;"flightSearchForm.controls.from.errors"&lt;/span&gt;
  &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the &lt;code&gt;aria-live&lt;/code&gt; attribute is set to &lt;code&gt;assertive&lt;/code&gt;, which means that the screen reader will announce the error message immediately when it appears. The &lt;code&gt;role="alert"&lt;/code&gt; indicates that this is an important message. Additionally, the &lt;code&gt;aria-invalid&lt;/code&gt; attribute is used to indicate that the input field has validation errors and the &lt;code&gt;aria-describedby&lt;/code&gt; attribute is used to associate the error message with the input field. Note that the &lt;code&gt;aria-describedby&lt;/code&gt; attribute is set to &lt;code&gt;null&lt;/code&gt; when there are no errors. This ensures that it's not present in the rendered DOM when not needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessibility Workshop
&lt;/h2&gt;

&lt;p&gt;For those looking to deepen their &lt;em&gt;Angular&lt;/em&gt; expertise, we offer a range of workshops – both in English and German:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;♿ &lt;a href="https://www.angulararchitects.io/en/training/angular-accessibility-workshop/" rel="noopener noreferrer"&gt;&lt;strong&gt;Accessibility Workshop&lt;/strong&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📈 &lt;a href="https://www.angulararchitects.io/en/training/angular-best-practices/" rel="noopener noreferrer"&gt;&lt;strong&gt;Best Practices Workshop&lt;/strong&gt;&lt;/a&gt; (including accessibility-related topics)&lt;/li&gt;
&lt;li&gt;🚀 &lt;a href="https://www.angulararchitects.io/en/training/angular-performance-optimization-workshop/" rel="noopener noreferrer"&gt;&lt;strong&gt;Performance Workshop&lt;/strong&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Implementing &lt;strong&gt;ARIA&lt;/strong&gt; in &lt;em&gt;Angular&lt;/em&gt; is essential for building web apps that are both accessible and inclusive. By leveraging &lt;strong&gt;ARIA roles and attributes&lt;/strong&gt; appropriately, developers can bridge gaps where native HTML falls short, ensuring that all users have a positive experience. This approach not only meets accessibility standards but also lays the foundation for user-centric and future-proof design.&lt;/p&gt;

&lt;p&gt;In the next edition of our &lt;a href="https://www.angulararchitects.io/blog/web-accessibility-in-angular/" rel="noopener noreferrer"&gt;&lt;strong&gt;A11y blog series&lt;/strong&gt;&lt;/a&gt;, we'll cover &lt;a href="https://www.angulararchitects.io/blog/accessible-angular-forms/" rel="noopener noreferrer"&gt;&lt;strong&gt;Accessible Angular Forms&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This blog post was written by &lt;a href="https://alex.thalhammer.name" rel="noopener noreferrer"&gt;Alexander Thalhammer&lt;/a&gt;. Follow me on &lt;a href="https://at.linkedin.com/in/thalhammer" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt;, &lt;a href="https://twitter.com/LX_T" rel="noopener noreferrer"&gt;X&lt;/a&gt; or &lt;a href="https://github.com/L-X-T" rel="noopener noreferrer"&gt;giThub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://blog.angular.dev/accessibility-in-angular-e84f73a223f" rel="noopener noreferrer"&gt;Accessibility in Angular Applications&lt;/a&gt; by Zama Khan Mohammed&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://amir-saeed.medium.com/accessibility-in-angular-17-bae5de7c2803" rel="noopener noreferrer"&gt;Accessibility in Angular 17&lt;/a&gt; by Amir Saeed&lt;/li&gt;
&lt;li&gt;&lt;a href="https://angular.dev/api/router/RouterLinkActive#ariaCurrentWhenActive" rel="noopener noreferrer"&gt;ariaCurrentWhenActive – official docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles" rel="noopener noreferrer"&gt;ARIA roles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes" rel="noopener noreferrer"&gt;ARIA attributes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>angular</category>
      <category>a11y</category>
    </item>
    <item>
      <title>Accessible Angular Routes</title>
      <dc:creator>Alexander Thalhammer</dc:creator>
      <pubDate>Tue, 27 May 2025 15:29:04 +0000</pubDate>
      <link>https://dev.to/lxt/accessible-angular-routes-53b</link>
      <guid>https://dev.to/lxt/accessible-angular-routes-53b</guid>
      <description>&lt;p&gt;This article explains how to use &lt;em&gt;Angular Router&lt;/em&gt; features to achieve quick wins in improving &lt;strong&gt;Accessibility (A11y)&lt;/strong&gt;. It is the third edition of our &lt;strong&gt;A11y blog series&lt;/strong&gt;. If you want to enhance your &lt;em&gt;Angular A11y&lt;/em&gt; skills, please make sure to read the other articles in this series as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Page Titles
&lt;/h2&gt;

&lt;p&gt;Does your &lt;em&gt;Angular App&lt;/em&gt; look like this if you open it in multiple tabs?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frj2d8ew5nyz5jqdlgz2b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frj2d8ew5nyz5jqdlgz2b.png" alt="Angular App without Page Titles" width="797" height="31"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If it does, you should definitely consider adding unique page titles for each route. The very easy-to-use &lt;code&gt;Route.title&lt;/code&gt; feature shipped with &lt;a href="https://x.com/twerske/status/1488313309644214272" rel="noopener noreferrer"&gt;Angular v14&lt;/a&gt; in 2022, yet many &lt;em&gt;Angular Developers&lt;/em&gt; are still not using it 😱. Please take a look and adopt it in your &lt;em&gt;Angular Apps&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Route.title
&lt;/h3&gt;

&lt;p&gt;Use this built-in &lt;code&gt;Router&lt;/code&gt; feature to automatically update the page &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; after each successful navigation, enhancing both accessibility and UX for all of us. To enable it in your primary &lt;code&gt;&amp;lt;router-outlet /&amp;gt;&lt;/code&gt;, you only need to set the &lt;code&gt;title&lt;/code&gt; property in your routes configuration array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Routes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;demo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Look how easy it is to use&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DemoComponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will update the page title in the browser tab and make it accessible to screen readers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs09rszrqu5p89uzk0pqe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs09rszrqu5p89uzk0pqe.png" alt="Page Title Demo" width="800" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In larger &lt;em&gt;Angular Apps&lt;/em&gt;, setting page titles can become inconsistent due to the lack of a common prefix or suffix for routes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Global Page Title Strategy
&lt;/h3&gt;

&lt;p&gt;To address this, you can extend &lt;em&gt;Angular's&lt;/em&gt; abstract &lt;code&gt;TitleStrategy&lt;/code&gt; class and implement your custom &lt;strong&gt;page title strategy&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// [imports]&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PageTitleStrategy&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;TitleStrategy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;updateTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;routerState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RouterStateSnapshot&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pageTitle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;buildTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;routerState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pageTitle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pageTitle&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; – Demo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Link to Demo below!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then add the custom title strategy to your &lt;code&gt;app.config.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// [imports]&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PageTitleStrategy&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./page-tite-strategy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ApplicationConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;provideClientHydration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;withIncrementalHydration&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
    &lt;span class="nf"&gt;provideExperimentalZonelessChangeDetection&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nf"&gt;provideRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TitleStrategy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;useClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PageTitleStrategy&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// add this line&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Dynamic Page Titles using Angular Router Params
&lt;/h3&gt;

&lt;p&gt;To create dynamic page titles using &lt;em&gt;Angular Router&lt;/em&gt; parameters, follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;First, we set up the &lt;code&gt;withComponentInputBinding()&lt;/code&gt; function to bind the route parameters to your component inputs:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// [imports]&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ApplicationConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;provideClientHydration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;withIncrementalHydration&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
    &lt;span class="nf"&gt;provideExperimentalZonelessChangeDetection&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nf"&gt;provideRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;withComponentInputBinding&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; &lt;span class="c1"&gt;// add feature to router&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TitleStrategy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;useClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PageTitleStrategy&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Then, we add the parameter to the route definition:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Routes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="c1"&gt;// [...]&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;demo/:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Demo #id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// note that this will be ignored and replaced by the dynamic title&lt;/span&gt;
    &lt;span class="na"&gt;loadComponent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./demo/demo.component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Now, we can simply add a &lt;code&gt;input&lt;/code&gt; signal to the component that will automatically receive the route param (thanks to the &lt;code&gt;withComponentInputBinding()&lt;/code&gt; feature):
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// [imports &amp;amp; decorator]&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DemoComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// [...]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;DemoComponent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Finally, we can use an &lt;code&gt;effect&lt;/code&gt; on our &lt;code&gt;id&lt;/code&gt; input signal to display the dynamic page title:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// [imports &amp;amp; decorator]&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DemoComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;effect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`Page #&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt; - Demo`&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Page - Demo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;DemoComponent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, when you navigate to &lt;code&gt;/demo/1&lt;/code&gt;, the page title will be set to &lt;code&gt;Page #1 - Demo&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  All Together Now
&lt;/h3&gt;

&lt;p&gt;To bring it all together – with the global page title strategy – we add another method to the &lt;code&gt;PageTitleStrategy&lt;/code&gt; class and provide it in &lt;code&gt;root&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// page-title-strategy.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RouterStateSnapshot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TitleStrategy&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/router&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Title&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/platform-browser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PageTitleStrategy&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;TitleStrategy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;updateTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;routerState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RouterStateSnapshot&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;buildTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;routerState&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;setTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pageTitle&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pageTitle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;pageTitle&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; – Demo`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Page title like a pro&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here is the final version of the dynamic page title &lt;code&gt;effect&lt;/code&gt; in the &lt;code&gt;DemoComponent&lt;/code&gt; using our injected &lt;code&gt;PageTitleStrategy&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// demo.component.ts&lt;/span&gt;
&lt;span class="c1"&gt;// [imports &amp;amp; decorator]&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DemoComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;pageTitleStrategy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PageTitleStrategy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;effect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pageTitleStrategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`Page #&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Page&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;DemoComponent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can set the page title depending on the route params and have a consistent suffix for all routes in your &lt;em&gt;Angular App&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkm1wf9obmlj617i79xwh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkm1wf9obmlj617i79xwh.png" alt="Dynamic Page Title Demo" width="800" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pretty nice, huh? Check out the full source code in the &lt;code&gt;title-strategy&lt;/code&gt; branch of &lt;a href="https://github.com/L-X-T/ssr-ih-ng19-days/tree/title-strategy" rel="noopener noreferrer"&gt;my GitHub repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  RouterLinkActive
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;RouterLinkActive&lt;/code&gt; directive is another powerful and lightweight tool for indicating the active state of navigation links in your &lt;em&gt;Angular App&lt;/em&gt;. It allows you to apply CSS styles to navigation links based on their active state, making it easier to create appealing and accessible menus.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- nav.component.html --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;nav&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;[routerLink]=&lt;/span&gt;&lt;span class="s"&gt;"demo"&lt;/span&gt; &lt;span class="na"&gt;routerLinkActive=&lt;/span&gt;&lt;span class="s"&gt;"active"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Demo&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;!&lt;/span&gt;&lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;nav&lt;/span&gt;&lt;span class="nc"&gt;.component.scss&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;nav&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="nc"&gt;.active&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--nav-link--active__color&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;text-decoration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Fine-tuned control with &lt;code&gt;routerLinkActiveOptions&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Since &lt;em&gt;Angular v14&lt;/em&gt;, you can use the &lt;code&gt;routerLinkActiveOptions&lt;/code&gt; directive to fine-tune the active state of your links. Where &lt;code&gt;options&lt;/code&gt; will have either the shape of &lt;a href="https://angular.dev/api/router/IsActiveMatchOptions" rel="noopener noreferrer"&gt;&lt;strong&gt;IsActiveMatchOptions&lt;/strong&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IsActiveMatchOptions&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;fragment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;exact&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ignored&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;matrixParams&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;exact&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;subset&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ignored&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;exact&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;subset&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;queryParams&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;exact&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;subset&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ignored&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, just a boolean named &lt;code&gt;exact&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;exact&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To apply exact matching, add the &lt;code&gt;routerLinkActiveOptions&lt;/code&gt; directive to your link:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- nav.component.html --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;nav&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt;
    &lt;span class="na"&gt;routerLink=&lt;/span&gt;&lt;span class="s"&gt;"/demo"&lt;/span&gt;
  &lt;span class="na"&gt;routerLinkActive=&lt;/span&gt;&lt;span class="s"&gt;"active"&lt;/span&gt;
  &lt;span class="na"&gt;[routerLinkActiveOptions]=&lt;/span&gt;&lt;span class="s"&gt;"{ exact: true }"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Demo
  &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Indicate current page &lt;code&gt;ariaCurrentWhenActive&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;A final quick win in &lt;em&gt;A11y in Angular&lt;/em&gt; is to highlight the &lt;strong&gt;current page link&lt;/strong&gt; in the nav for screen readers via &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-current" rel="noopener noreferrer"&gt;aria-current&lt;/a&gt;: We need to add the &lt;code&gt;aria-current="page"&lt;/code&gt; attribute. This can easily be done using the &lt;a href="https://angular.dev/best-practices/a11y#active-links-identification" rel="noopener noreferrer"&gt;&lt;strong&gt;ariaCurrentWhenActive&lt;/strong&gt;&lt;/a&gt; input on the &lt;code&gt;routerLinkActive&lt;/code&gt; directive by setting its value to "page":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- nav.component.html --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;nav&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt;
    &lt;span class="na"&gt;routerLink=&lt;/span&gt;&lt;span class="s"&gt;"/demo"&lt;/span&gt;
    &lt;span class="na"&gt;routerLinkActive=&lt;/span&gt;&lt;span class="s"&gt;"active"&lt;/span&gt;
    &lt;span class="na"&gt;[routerLinkActiveOptions]=&lt;/span&gt;&lt;span class="s"&gt;"{ exact: true }"&lt;/span&gt;
    &lt;span class="na"&gt;ariaCurrentWhenActive=&lt;/span&gt;&lt;span class="s"&gt;"page"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Demo
  &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Accessibility Workshop
&lt;/h2&gt;

&lt;p&gt;For those looking to deepen their &lt;em&gt;Angular&lt;/em&gt; expertise, we offer a range of workshops – both in English and German:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;♿ &lt;a href="https://www.angulararchitects.io/en/training/angular-accessibility-workshop/" rel="noopener noreferrer"&gt;&lt;strong&gt;Accessibility Workshop&lt;/strong&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📈 &lt;a href="https://www.angulararchitects.io/en/training/angular-best-practices/" rel="noopener noreferrer"&gt;&lt;strong&gt;Best Practices Workshop&lt;/strong&gt;&lt;/a&gt; (including accessibility related topics)&lt;/li&gt;
&lt;li&gt;🚀 &lt;a href="https://www.angulararchitects.io/en/training/angular-performance-optimization-workshop/" rel="noopener noreferrer"&gt;&lt;strong&gt;Performance Workshop&lt;/strong&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Implementing &lt;em&gt;Angular's&lt;/em&gt; built-in router features is a simple yet powerful way to boost your A11y 🚀&lt;/p&gt;

&lt;p&gt;By leveraging dynamic page titles, a global title strategy, and fine-tuning active link indicators with &lt;code&gt;RouterLinkActive&lt;/code&gt; and &lt;code&gt;ariaCurrentWhenActive&lt;/code&gt;, you can create a more inclusive UX that benefits everyone – from seasoned Devs (like me 😂) to users who rely on assistive technologies. These strategies not only improve UX but also help standardize navigation and page management across all &lt;em&gt;Angular Apps&lt;/em&gt;. Back to our example from the beginning, now our browser tab bar looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg78kp6m39italf90q721.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg78kp6m39italf90q721.png" alt="Angular App with Page Titles" width="797" height="31"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I think this is a great example of a quick win! Keep playing with these features and explore the rest of our A11y blog series for more insights on building accessible, high-performing web apps.&lt;/p&gt;

&lt;p&gt;In the next edition of our &lt;strong&gt;A11y blog series&lt;/strong&gt;, we'll cover more &lt;strong&gt;&lt;a href="https://www.angulararchitects.io/blog/aria-roles-attributes/" rel="noopener noreferrer"&gt;ARIA roles and attributes&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This blog post was written by &lt;a href="https://alex.thalhammer.name" rel="noopener noreferrer"&gt;Alexander Thalhammer&lt;/a&gt;. Follow me on &lt;a href="https://at.linkedin.com/in/thalhammer" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt;, &lt;a href="https://twitter.com/LX_T" rel="noopener noreferrer"&gt;X&lt;/a&gt; or &lt;a href="https://github.com/L-X-T" rel="noopener noreferrer"&gt;giThub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/L-X-T/ssr-ih-ng19-days/tree/title-strategy" rel="noopener noreferrer"&gt;GitHub repo of demo&lt;/a&gt;, by Alexander Thalhammer&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://blog.angular.dev/angular-v14-is-now-available-391a6db736af" rel="noopener noreferrer"&gt;Angular v14 – official blog post&lt;/a&gt; by Emma Twersky&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://x.com/twerske/status/1488277224008478721" rel="noopener noreferrer"&gt;Tweet about Route title&lt;/a&gt; by Emma Twersky&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/brandontroberts/setting-page-titles-natively-with-the-angular-router-393j"&gt;Page Titles With The Router&lt;/a&gt; by Brandon Roberts&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://briantree.se/angular-tutorial-router-link-and-accessibility/" rel="noopener noreferrer"&gt;Router Link Accessibility Features&lt;/a&gt; by Brian Treese&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/thisdotmedia/make-it-accessible-navigation-in-angular-2gee"&gt;Make it accessible: Navigation in Angular&lt;/a&gt; by Daniel Marin&lt;/li&gt;
&lt;li&gt;&lt;a href="https://angular.dev/api/router/RouterLinkActive#ariaCurrentWhenActive" rel="noopener noreferrer"&gt;ariaCurrentWhenActive – official docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>angular</category>
      <category>a11y</category>
    </item>
  </channel>
</rss>
