<?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: Srikar Phani Kumar Marti</title>
    <description>The latest articles on DEV Community by Srikar Phani Kumar Marti (@mspk97).</description>
    <link>https://dev.to/mspk97</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%2F2519231%2F04ef8b8c-91fd-4ba1-a9b7-1e98f84baf6a.png</url>
      <title>DEV Community: Srikar Phani Kumar Marti</title>
      <link>https://dev.to/mspk97</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mspk97"/>
    <language>en</language>
    <item>
      <title>Why Input Latency Symptoms Can Mislead Developers: A Deep Dive Into the Real Causes</title>
      <dc:creator>Srikar Phani Kumar Marti</dc:creator>
      <pubDate>Mon, 26 Oct 2026 13:02:03 +0000</pubDate>
      <link>https://dev.to/mspk97/why-input-latency-symptoms-can-mislead-developers-a-deep-dive-into-the-real-causes-1hf</link>
      <guid>https://dev.to/mspk97/why-input-latency-symptoms-can-mislead-developers-a-deep-dive-into-the-real-causes-1hf</guid>
      <description>&lt;p&gt;You’re clicking a button or typing in a form, and the UI feels sluggish. Maybe the button highlight or input response lags behind your finger or mouse by a noticeable fraction of a second. You open your profiler, see some long frames, and assume the culprit is obvious: your JavaScript is blocking the main thread. But wait, digging deeper, the cause isn’t always what it seems.&lt;/p&gt;

&lt;p&gt;I ran into this exact confusion debugging a web app where users complained about input lag. Profiling showed long tasks, but the input event timestamps didn’t line up with when UI updates actually happened. The symptoms pointed fingers at JavaScript execution, but it turned out the real bottleneck was a mix of event handling delays, rendering pipeline quirks, and misunderstood profiling signals.&lt;/p&gt;

&lt;p&gt;Let’s walk through how browsers handle input events, rendering, and JavaScript work together ,  and why input latency symptoms can mislead you.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Familiar Symptom: Input Feels Sluggish
&lt;/h2&gt;

&lt;p&gt;Imagine this: you press a key or tap a button. The app should respond immediately ,  highlight the button, update text, whatever. Instead, something feels off:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The UI update happens noticeably after your input.&lt;/li&gt;
&lt;li&gt;Sometimes the delay is consistent; other times, it’s jittery.&lt;/li&gt;
&lt;li&gt;Profiling shows long JavaScript tasks around input, but it’s unclear if that’s cause or effect.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You might think the fix is straightforward: optimize your JS handlers or throttle expensive logic. But what if the event isn’t even reaching your code on time? Or the rendering pipeline itself is introducing delays?&lt;/p&gt;

&lt;h2&gt;
  
  
  Input Events Aren’t Instant: The Browser Event Pipeline
&lt;/h2&gt;

&lt;p&gt;Browsers don’t just fire an event the moment your finger touches the screen. Input events travel through multiple phases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Raw Input Processing:&lt;/strong&gt; The OS and browser receive hardware signals ,  touch, mouse, keyboard.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event Coalescing:&lt;/strong&gt; For pointer and touch events, browsers may batch or coalesce multiple events into one to avoid flooding your app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event Dispatch:&lt;/strong&gt; The browser fires DOM events on appropriate targets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event Handling:&lt;/strong&gt; Your JavaScript listeners run.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This pipeline can add subtle delays:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On mobile, touch events can be delayed by about 300ms historically to detect double taps (though this is largely fixed with pointer events).&lt;/li&gt;
&lt;li&gt;Coalescing means you might get fewer events but some representing multiple physical inputs, causing perceived latency.&lt;/li&gt;
&lt;li&gt;If the main thread is busy, event callbacks queue up and run late.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So even before your handler runs, the event might already be delayed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rendering Pipeline Adds Another Layer of Delay
&lt;/h2&gt;

&lt;p&gt;After your JavaScript updates state or DOM, the browser proceeds through rendering steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Style Recalculation:&lt;/strong&gt; Determine affected CSS styles.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Layout:&lt;/strong&gt; Compute positions and sizes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Paint:&lt;/strong&gt; Draw pixels.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composite:&lt;/strong&gt; Combine layers for final display.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These steps happen asynchronously after your JS task finishes. If your JS blocks the main thread for too long, rendering waits. But even if JS is quick, heavy style/layout/paint jobs can delay frame updates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Profiling Can Lie: Understanding Timeline Marks
&lt;/h2&gt;

&lt;p&gt;When you look at DevTools performance traces, several marks confuse newcomers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Event Timing:&lt;/strong&gt; Shows when an event fired and how long its handler took.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frames:&lt;/strong&gt; Show when the browser painted new content.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Long Tasks:&lt;/strong&gt; Blocks that keep the main thread busy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s tempting to blame the longest task overlapping the input event, but remember:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your event handler may run late due to earlier tasks.&lt;/li&gt;
&lt;li&gt;Rendering work triggered by your handler runs &lt;em&gt;after&lt;/em&gt; JS finishes.&lt;/li&gt;
&lt;li&gt;Input latency includes both event delay and render delay.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, a long style recalculation after your handler ends can cause the visible UI update to lag even if JS was fast.&lt;/p&gt;

&lt;h2&gt;
  
  
  JavaScript Execution Isn’t the Whole Story
&lt;/h2&gt;

&lt;p&gt;In one recent bug, I found a React app where input felt delayed. Profiling showed 50ms event handlers and 100ms layout recalculations after. Initially, I thought throttling or debouncing input handlers would help.&lt;/p&gt;

&lt;p&gt;But the real gain came from splitting heavy CSS selectors to speed layout and adding &lt;code&gt;will-change&lt;/code&gt; to trigger compositing layers. The rendering work was the actual bottleneck, not JS.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Can Do to Pinpoint the Real Bottleneck
&lt;/h2&gt;

&lt;p&gt;Start with the profiler timeline:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check when the input event fired vs. when the handler ran. If there’s a gap, the main thread was busy.&lt;/li&gt;
&lt;li&gt;Look at style/layout/paint work after the handler. Long layout means your DOM or CSS is expensive.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;Event Timing API&lt;/strong&gt; (&lt;code&gt;event.timeStamp&lt;/code&gt; and &lt;code&gt;performance.now()&lt;/code&gt;) to measure event latency from user input.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Try isolating:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run with minimal JavaScript handlers to see if input latency persists.&lt;/li&gt;
&lt;li&gt;Simplify CSS and DOM to measure layout cost.&lt;/li&gt;
&lt;li&gt;Use Chrome’s &lt;strong&gt;Rendering&lt;/strong&gt; panel to visualize paint and composite layers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Common Pitfalls That Mislead Developers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Blaming expensive JS exclusively:&lt;/strong&gt; Sometimes JS is fast, but layout or paint is slow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ignoring event coalescing or pointer event quirks:&lt;/strong&gt; These can cause input events to batch, delaying your handler.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Misreading profiler timelines:&lt;/strong&gt; Overlapping long tasks don’t always cause visible lag.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Neglecting browser optimizations and heuristics:&lt;/strong&gt; Browsers may delay rendering or event firing to optimize battery or CPU usage.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Input latency feels simple until you peek under the hood. It’s a dance between hardware input, event processing, JavaScript execution, and rendering steps. Each layer can add delay, and symptoms that look like JS slowness may really be rendering or event dispatch subtleties.&lt;/p&gt;

&lt;p&gt;Next time input feels slow, don’t just blame your handlers. Look closely at event timing, rendering workload, and how the browser pipeline orchestrates everything. With that understanding, you’ll find the real bottlenecks faster and fix input lag for good.&lt;/p&gt;




&lt;h2&gt;
  
  
  Helpful learning resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/" rel="noopener noreferrer"&gt;MDN Web Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web.dev/" rel="noopener noreferrer"&gt;web.dev performance guidance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://react.dev/" rel="noopener noreferrer"&gt;React documentation&lt;/a&gt;
Originally published at &lt;a href="https://blog.mspk.me/posts/why-input-latency-symptoms-can-mislead-developers-a-deep-dive-into-the-real-causes" rel="noopener noreferrer"&gt;Under The Hood&lt;/a&gt;.
Get the next deep dive in your inbox: &lt;a href="https://blog.mspk.me/#subscribe" rel="noopener noreferrer"&gt;subscribe to Under The Hood&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>frontendperformance</category>
      <category>inputlatency</category>
      <category>debugging</category>
      <category>rendering</category>
    </item>
    <item>
      <title>Why Signal Forms Matter in Angular: The Subtle Traps and How to Avoid Them</title>
      <dc:creator>Srikar Phani Kumar Marti</dc:creator>
      <pubDate>Thu, 22 Oct 2026 10:46:41 +0000</pubDate>
      <link>https://dev.to/mspk97/why-signal-forms-matter-in-angular-the-subtle-traps-and-how-to-avoid-them-5be2</link>
      <guid>https://dev.to/mspk97/why-signal-forms-matter-in-angular-the-subtle-traps-and-how-to-avoid-them-5be2</guid>
      <description>&lt;p&gt;You’ve been there: your Angular component’s UI stubbornly refuses to update even though you’re sure you changed the signal’s value. No errors, no crashes, just silence.&lt;/p&gt;

&lt;p&gt;I had this happen recently while refactoring a component’s state to use Angular Signals. I was updating a signal that held an object, but the UI didn’t react. After some head-scratching, I realized the problem wasn’t Angular’s reactivity itself but the &lt;strong&gt;form of the signal&lt;/strong&gt;, how the signal stored and exposed its value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signals Aren’t Just Boxes: They Have Forms
&lt;/h2&gt;

&lt;p&gt;At first glance, a signal is just a container for a value you can get or set. But under the hood, Angular Signals can hold primitives, objects, arrays, or even computed values. How this data is shaped and updated determines if Angular’s change detection triggers.&lt;/p&gt;

&lt;p&gt;To put it simply, the &lt;strong&gt;form of a signal&lt;/strong&gt; is about what kind of value it emits and how you update it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;primitive signal&lt;/strong&gt; holds a string, number, boolean, etc.&lt;/li&gt;
&lt;li&gt;An &lt;strong&gt;object signal&lt;/strong&gt; holds a reference to an object.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;computed signal&lt;/strong&gt; derives its value from other signals.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Signal Form Tripped Me Up: The Object Reference Gotcha
&lt;/h2&gt;

&lt;p&gt;Here’s a minimal reproduction of what I ran into:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Alice&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Later...&lt;/span&gt;
&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Mutating the nested property&lt;/span&gt;
&lt;span class="c1"&gt;// user.set(user()); // Not called&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I changed &lt;code&gt;user().age&lt;/code&gt;, expecting Angular to detect that the &lt;code&gt;user&lt;/code&gt; signal changed and update the UI. But it didn’t. Why?&lt;/p&gt;

&lt;p&gt;Angular’s Signals track changes using &lt;strong&gt;reference equality&lt;/strong&gt;. When you mutate a nested property inside the object but don’t assign a new object to the signal, Angular has no way to know anything changed.&lt;/p&gt;

&lt;p&gt;So just mutating the object’s property is invisible to Angular's tracking.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Fix This: Always Update the Signal with a New Reference
&lt;/h2&gt;

&lt;p&gt;To trigger updates, you have to replace the signal’s value with a new object:&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="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;31&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a new object, and Angular sees the reference changed, so it triggers updates.&lt;/p&gt;

&lt;h2&gt;
  
  
  What About Computed Signals?
&lt;/h2&gt;

&lt;p&gt;Computed signals derive their value from other signals and update automatically when dependencies change.&lt;/p&gt;

&lt;p&gt;Example:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&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;double&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;double&lt;/code&gt; recomputes whenever &lt;code&gt;count&lt;/code&gt; changes. But if &lt;code&gt;count&lt;/code&gt; holds an object and you mutate it without changing the reference, &lt;code&gt;double&lt;/code&gt; won’t update either.&lt;/p&gt;

&lt;p&gt;So the same reference rule applies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signal Forms in Templates: Why Reading Matters
&lt;/h2&gt;

&lt;p&gt;Another subtlety: how you &lt;strong&gt;read&lt;/strong&gt; a signal impacts dependency tracking.&lt;/p&gt;

&lt;p&gt;In your template, you typically call the signal as a function: &lt;code&gt;{{ user().name }}&lt;/code&gt;. This registers a dependency on &lt;code&gt;user&lt;/code&gt;. But if you read nested properties outside Angular’s reactive context or in plain functions, the dependency won’t be tracked.&lt;/p&gt;

&lt;p&gt;For example, if you extract &lt;code&gt;const name = user().name&lt;/code&gt; in a non-reactive function, Angular won’t know to update when &lt;code&gt;user&lt;/code&gt; changes.&lt;/p&gt;

&lt;p&gt;So &lt;strong&gt;always read signals inside reactive contexts&lt;/strong&gt; (templates, computed signals, effect functions).&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical Tips to Avoid Signal Form Bugs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Treat signals holding objects as immutable.&lt;/strong&gt; Always replace the whole object rather than mutating it in place.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use computed signals to derive values&lt;/strong&gt; instead of manually reading and mutating nested properties.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avoid side effects or mutations inside signals’ values.&lt;/strong&gt; If you need to update deeply nested state, consider immutable updates or helper libraries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read signals only inside reactive contexts.&lt;/strong&gt; Don’t cache nested properties in non-reactive code.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When to Use Different Signal Forms
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;primitive signals&lt;/strong&gt; for simple values like counters, booleans, strings.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;object signals&lt;/strong&gt; for state objects but update immutably.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;computed signals&lt;/strong&gt; to derive data you don’t want to manage manually.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, managing a form input state:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;updateFirstName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&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="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;formState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;formState&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;value&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;h2&gt;
  
  
  What Happens if You Don’t Follow Signal Forms?
&lt;/h2&gt;

&lt;p&gt;You’ll face frustrating UI bugs where the view never updates or updates inconsistently. Debugging these can be tricky because Angular won’t warn you about mutations inside signal values.&lt;/p&gt;

&lt;p&gt;Sometimes you might see stale data in your components or computed signals not re-evaluating.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Angular Signals are powerful, but their reactivity depends heavily on &lt;strong&gt;how you shape and update their values&lt;/strong&gt;. Treat signals as immutable containers and update them by replacing values, not mutating them. Keep your reads inside reactive contexts.&lt;/p&gt;

&lt;p&gt;Once you get this, your Angular apps will feel more predictable, and those silent update bugs will become a thing of the past.&lt;/p&gt;

&lt;p&gt;If you’ve struggled with signals not updating, check the form of your signal values first. The fix might be simpler than you think.&lt;/p&gt;




&lt;h2&gt;
  
  
  Helpful learning resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/" rel="noopener noreferrer"&gt;MDN Web Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://react.dev/" rel="noopener noreferrer"&gt;React documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://platform.openai.com/docs" rel="noopener noreferrer"&gt;OpenAI developer resources&lt;/a&gt;
Originally published at &lt;a href="https://blog.mspk.me/posts/why-signal-forms-matter-in-angular-the-subtle-traps-and-how-to-avoid-them" rel="noopener noreferrer"&gt;Under The Hood&lt;/a&gt;.
Get the next deep dive in your inbox: &lt;a href="https://blog.mspk.me/#subscribe" rel="noopener noreferrer"&gt;subscribe to Under The Hood&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>angular</category>
      <category>signals</category>
      <category>reactivity</category>
      <category>frontend</category>
    </item>
    <item>
      <title>CSS Logical Properties: How Browsers Translate Them to Physical Styles</title>
      <dc:creator>Srikar Phani Kumar Marti</dc:creator>
      <pubDate>Wed, 21 Oct 2026 10:52:22 +0000</pubDate>
      <link>https://dev.to/mspk97/css-logical-properties-how-browsers-translate-them-to-physical-styles-159a</link>
      <guid>https://dev.to/mspk97/css-logical-properties-how-browsers-translate-them-to-physical-styles-159a</guid>
      <description>&lt;p&gt;You’ve probably hit this subtle bug before: you write some CSS using logical properties like &lt;code&gt;margin-inline-start&lt;/code&gt;, expecting your layout to just work regardless of text direction. But then you flip your app to RTL or switch writing modes, and suddenly things don’t line up as you expected. Margins that should shift to the left stay put, or worse, shift in the wrong direction.&lt;/p&gt;

&lt;p&gt;I ran into this during a recent localization sprint. Our UI had mixed LTR and RTL content, and rather than duplicating styles or using awkward hacks, I leaned on CSS logical properties. They’re supposed to abstract away physical directions like left/right and top/bottom. But debugging why my margins weren’t behaving as I imagined forced me to dig under the hood ,  how exactly do browsers turn these logical properties into physical ones?&lt;/p&gt;

&lt;h2&gt;
  
  
  Logical vs Physical CSS Properties: The Basic Idea
&lt;/h2&gt;

&lt;p&gt;Traditional CSS uses physical directions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;margin-left&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;10&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;padding-top&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;5&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are fixed to the box’s edges, regardless of the document’s writing mode (LTR or RTL) or layout direction (horizontal or vertical).&lt;/p&gt;

&lt;p&gt;Logical properties, introduced in CSS Logical Properties and Values Module, let you specify styles based on the writing mode and flow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;margin-inline-start&lt;/code&gt; applies margin at the start of the inline flow&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;padding-block-end&lt;/code&gt; applies padding at the end of the block flow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So in a left-to-right language, &lt;code&gt;margin-inline-start&lt;/code&gt; equals &lt;code&gt;margin-left&lt;/code&gt;. In right-to-left, it maps to &lt;code&gt;margin-right&lt;/code&gt;. This makes your CSS more adaptable without duplication.&lt;/p&gt;

&lt;p&gt;But here’s the kicker: browsers do this mapping internally, translating your logical properties to physical ones at layout time. Understanding that translation explains many layout quirks.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Browsers Translate Logical Properties
&lt;/h2&gt;

&lt;p&gt;When the browser’s CSS engine parses a rule like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.my-box&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;margin-inline-start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&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;it doesn’t leave it as &lt;code&gt;margin-inline-start&lt;/code&gt;. Instead, it figures out the physical edge corresponding to "inline start" for the element’s writing mode and direction.&lt;/p&gt;

&lt;p&gt;For example, suppose your document is LTR horizontal:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;inline direction goes left to right&lt;/li&gt;
&lt;li&gt;inline start is left&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The browser will apply the margin as &lt;code&gt;margin-left: 16px&lt;/code&gt; internally. But if the same element is in RTL horizontal:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;inline direction is right to left&lt;/li&gt;
&lt;li&gt;inline start is right&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now the browser applies &lt;code&gt;margin-right: 16px&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This mapping happens during style computation, after resolving the element’s &lt;code&gt;writing-mode&lt;/code&gt; and &lt;code&gt;direction&lt;/code&gt; but before layout.&lt;/p&gt;

&lt;h2&gt;
  
  
  What about vertical writing modes?
&lt;/h2&gt;

&lt;p&gt;When the writing mode is vertical (like Japanese or Mongolian vertical text), "inline" and "block" directions swap:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Inline flows top to bottom or bottom to top&lt;/li&gt;
&lt;li&gt;Block flows left to right or right to left&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So &lt;code&gt;margin-inline-start&lt;/code&gt; could map to &lt;code&gt;margin-top&lt;/code&gt; or &lt;code&gt;margin-bottom&lt;/code&gt; depending on &lt;code&gt;writing-mode&lt;/code&gt; and &lt;code&gt;direction&lt;/code&gt;. This can be surprisingly confusing if you only think in LTR horizontal terms.&lt;/p&gt;

&lt;h2&gt;
  
  
  Behind the Scenes: The Browser’s Style Computation Pipeline
&lt;/h2&gt;

&lt;p&gt;In the browser’s rendering engine (think Blink, Gecko, or WebKit), style computation is a multi-step process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Parsing CSS&lt;/strong&gt; ,  The engine parses your CSS, creating a style tree where each node has declarations like &lt;code&gt;margin-inline-start&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Resolving Computed Styles&lt;/strong&gt; ,  The engine resolves computed values for each property. For logical properties, it uses the element’s computed &lt;code&gt;writing-mode&lt;/code&gt; and &lt;code&gt;direction&lt;/code&gt; to map logical properties to physical ones.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Applying Physical Styles&lt;/strong&gt; ,  The engine applies these physical styles to layout boxes, which then participate in the box model and flow layout calculations.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This means that from the layout engine’s perspective, it never really sees &lt;code&gt;margin-inline-start&lt;/code&gt;; it only sees &lt;code&gt;margin-left&lt;/code&gt; or &lt;code&gt;margin-top&lt;/code&gt;, etc., depending on context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters for Debugging Layout Issues
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Unexpected Margins on RTL or Vertical Text&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If your app flips to RTL but some margins don’t move as expected, check if you’re mixing logical and physical properties. Physical properties like &lt;code&gt;margin-left&lt;/code&gt; don’t adapt, so your layout breaks.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Overriding Logical Properties with Physical Ones&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Sometimes, authors override logical properties with physical ones elsewhere in CSS. Because physical properties have the same specificity and cascade rules, the last one wins. This can cause logical properties to be silently ignored.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;DevTools Show Physical Properties&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Inspecting elements in browser DevTools often shows the physical properties after translation, not the logical ones you wrote. This can confuse you into thinking your CSS isn’t applied.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical Tips When Using Logical Properties
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Always use logical properties consistently&lt;/strong&gt; in your CSS for layout margins, paddings, borders, and positioning.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Be mindful of writing mode&lt;/strong&gt;. If your app supports vertical writing modes, test your layouts carefully. Logical properties behave differently there.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Check DevTools carefully&lt;/strong&gt;. Don’t expect to see &lt;code&gt;margin-inline-start&lt;/code&gt; in computed styles. Look for the mapped physical properties.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use computed styles and layout debugging tools&lt;/strong&gt;. Some browsers let you visualize box model edges and directions, which can help understand how logical properties translate.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Avoid mixing physical and logical properties for the same edge&lt;/strong&gt;. This reduces cascade confusion.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A Real Debugging Example
&lt;/h2&gt;

&lt;p&gt;I had a sidebar that used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.sidebar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;padding-inline-start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;border-inline-end&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#ccc&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;It looked perfect in LTR, but in RTL, the padding stayed on the left, and the border appeared on the left instead of the right. Turns out, somewhere else in the stylesheet, there was a physical property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.sidebar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&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;Because &lt;code&gt;padding-left&lt;/code&gt; came after in the cascade, it silently overrode the logical padding on the physical left edge.&lt;/p&gt;

&lt;p&gt;Removing the physical property and relying solely on logical properties fixed the problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap-up
&lt;/h2&gt;

&lt;p&gt;CSS logical properties are a powerful way to write adaptable, locale-friendly styles. But they’re not magic. Browsers internally translate them to physical properties based on the element’s writing mode and direction, and understanding that helps you debug those sticky layout issues in multilingual or vertical writing layouts.&lt;/p&gt;

&lt;p&gt;Next time your &lt;code&gt;margin-inline-start&lt;/code&gt; feels stubborn, remember: it’s actually a &lt;code&gt;margin-left&lt;/code&gt; or &lt;code&gt;margin-right&lt;/code&gt; in disguise, chosen based on your document’s flow.&lt;/p&gt;

&lt;p&gt;Happy styling!&lt;/p&gt;




&lt;h2&gt;
  
  
  Helpful learning resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/" rel="noopener noreferrer"&gt;MDN Web Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web.dev/" rel="noopener noreferrer"&gt;web.dev performance guidance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://platform.openai.com/docs" rel="noopener noreferrer"&gt;OpenAI developer resources&lt;/a&gt;
Originally published at &lt;a href="https://blog.mspk.me/posts/css-logical-properties-how-browsers-translate-them-to-physical-styles" rel="noopener noreferrer"&gt;Under The Hood&lt;/a&gt;.
Get the next deep dive in your inbox: &lt;a href="https://blog.mspk.me/#subscribe" rel="noopener noreferrer"&gt;subscribe to Under The Hood&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>css</category>
      <category>layout</category>
      <category>internationalization</category>
      <category>webdev</category>
    </item>
    <item>
      <title>The Real Cost of CSS Variables: Runtime Resolution and Performance Implications</title>
      <dc:creator>Srikar Phani Kumar Marti</dc:creator>
      <pubDate>Tue, 20 Oct 2026 11:55:30 +0000</pubDate>
      <link>https://dev.to/mspk97/the-real-cost-of-css-variables-runtime-resolution-and-performance-implications-3lbo</link>
      <guid>https://dev.to/mspk97/the-real-cost-of-css-variables-runtime-resolution-and-performance-implications-3lbo</guid>
      <description>&lt;p&gt;You’ve probably felt it before: you write a neat CSS variable to keep your colors or spacing consistent, only to notice your page feels sluggish during some interactions. Maybe animations stutter, or style recalculations take longer than expected. It’s tempting to assume CSS variables are just a handy, cost-free abstraction, but under the hood they have real runtime costs that can sneak up on you.&lt;/p&gt;

&lt;p&gt;I hit this wall while optimizing a complex web app where CSS variables were used extensively ,  not just for theming but also for layout tweaks. At first, I thought the slowdowns were unrelated, maybe a heavy JS bundle or too many DOM nodes. But profiling told a different story: style recalculations spiked and correlated tightly with changes to CSS variables.&lt;/p&gt;

&lt;p&gt;Let’s walk through what actually happens inside browsers when you use CSS custom properties, why their runtime resolution matters, and how to balance convenience with performance in production apps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why CSS variables aren’t just static values
&lt;/h2&gt;

&lt;p&gt;Unlike regular CSS properties with fixed values like &lt;code&gt;color: red&lt;/code&gt;, CSS custom properties (variables) are dynamic. They’re defined like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--main-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#3498db&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.button&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;--main-color&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;At first glance, this looks simple: define once, reuse many times. But here’s the twist: browsers don’t replace &lt;code&gt;var(--main-color)&lt;/code&gt; with &lt;code&gt;#3498db&lt;/code&gt; at parse time. Instead, the actual value gets resolved during style computation at runtime.&lt;/p&gt;

&lt;p&gt;Why? Because CSS variables can cascade, inherit, and be overridden anywhere in the DOM tree. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.section1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--main-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#e74c3c&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;If a &lt;code&gt;.button&lt;/code&gt; is inside &lt;code&gt;.section1&lt;/code&gt;, its &lt;code&gt;var(--main-color)&lt;/code&gt; resolves to red, not blue. That means the browser has to look up the DOM tree to find the closest variable definition each time it computes styles.&lt;/p&gt;

&lt;h2&gt;
  
  
  What happens when the browser resolves CSS variables
&lt;/h2&gt;

&lt;p&gt;The browser’s style engine maintains a cascade and inheritance graph. When it encounters a &lt;code&gt;var()&lt;/code&gt; reference, it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Checks the element’s inline styles and stylesheets for a matching &lt;code&gt;--variable&lt;/code&gt; declaration.&lt;/li&gt;
&lt;li&gt;If not found, climbs up the DOM tree to parent elements, looking for a value.&lt;/li&gt;
&lt;li&gt;Uses the first defined value it finds.&lt;/li&gt;
&lt;li&gt;If none is found, applies fallback if provided, or invalidates the property.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This lookup isn’t free. It happens every time styles are recalculated, which can be frequent during layout shifts, animations, or DOM changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why style recalculations get expensive with many variables
&lt;/h2&gt;

&lt;p&gt;Imagine you have hundreds of elements using variables like &lt;code&gt;var(--spacing)&lt;/code&gt;, &lt;code&gt;var(--color)&lt;/code&gt;, or &lt;code&gt;var(--font-size)&lt;/code&gt;. When a variable value changes on a parent (say via a theme switch or a CSS class toggle), the browser must:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Invalidate styles of all elements affected by that variable.&lt;/li&gt;
&lt;li&gt;Re-resolve the variable value for each element, performing the DOM climb repeatedly.&lt;/li&gt;
&lt;li&gt;Update the computed style map and potentially trigger layout or paint updates.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This can cause a cascade of work that quickly adds up, especially if variables are used in properties that impact layout (like margins, widths) or paint (like colors).&lt;/p&gt;

&lt;p&gt;I found this by profiling in Chrome DevTools’ Performance tab. Style recalculation times ballooned when toggling a theme color variable that was referenced deep in the DOM tree.&lt;/p&gt;

&lt;h2&gt;
  
  
  Variables in animations: a double-edged sword
&lt;/h2&gt;

&lt;p&gt;CSS variables shine for theming and responsive design, but mixing them into animations can cause trouble.&lt;/p&gt;

&lt;p&gt;If your animation changes a CSS variable value on a root or container element, every dependent property on all children potentially needs re-resolving every frame. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;pulseColor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;--main-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#3498db&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="err"&gt;50&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;--main-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#e74c3c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="err"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;--main-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#3498db&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;Applying this animation to a top-level container means the browser must recompute styles for all descendants referencing &lt;code&gt;--main-color&lt;/code&gt; on every animation frame. That can kill frame rates.&lt;/p&gt;

&lt;p&gt;If you instead animate properties directly (like &lt;code&gt;background-color&lt;/code&gt;), the browser can often optimize and GPU-accelerate the paint without full style recomputations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which CSS properties amplify the cost?
&lt;/h2&gt;

&lt;p&gt;Not all properties are equal. When a CSS variable is used in a property that affects layout or paint, style recalcs can trigger more expensive downstream work.&lt;/p&gt;

&lt;p&gt;For example, variables used in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;width&lt;/code&gt;, &lt;code&gt;height&lt;/code&gt;, &lt;code&gt;margin&lt;/code&gt;, &lt;code&gt;padding&lt;/code&gt; → can cause layout recalculations&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;color&lt;/code&gt;, &lt;code&gt;background-color&lt;/code&gt; → cause paint invalidations&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;transform&lt;/code&gt;, &lt;code&gt;opacity&lt;/code&gt; → usually handled by compositor, less costly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using variables in properties that trigger layout recalcs can multiply the performance cost because layout affects positioning and size, cascading through the render tree.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I balanced CSS variables with performance
&lt;/h2&gt;

&lt;p&gt;After pinpointing CSS variable resolution as a bottleneck, I took several practical steps:&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Limit variable scope
&lt;/h2&gt;

&lt;p&gt;Avoid setting variables on very high-level elements like &lt;code&gt;:root&lt;/code&gt; if you don’t need to. Instead, scope them to smaller subtrees where they’re used. This reduces how many elements get invalidated when a variable changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Cache computed values in CSS or JS
&lt;/h2&gt;

&lt;p&gt;For values that rarely change but are performance critical, consider replacing variables with static values or compute and inject styles via JS if dynamic recalculation is too costly.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Avoid animating variables for large subtrees
&lt;/h2&gt;

&lt;p&gt;Reserve CSS variable animations for small, isolated parts of the DOM. For global animations, animate properties directly when possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Use DevTools to profile style recalcs
&lt;/h2&gt;

&lt;p&gt;Modern browser DevTools can help you see exactly how long style recalculations take and what triggers them. This helps catch expensive variable usage early.&lt;/p&gt;

&lt;h2&gt;
  
  
  When CSS variables still win
&lt;/h2&gt;

&lt;p&gt;Despite the cost, CSS variables provide unmatched flexibility, especially for theming, design tokens, and building components that adapt to context. They’re a powerful tool ,  but like all tools, you need to wield them with care.&lt;/p&gt;

&lt;p&gt;Knowing what’s going on under the hood helps you decide when variables make sense and when static styles or JavaScript-driven updates might be better.&lt;/p&gt;

&lt;p&gt;Next time your app feels sluggish after a style change, consider that it might be your variables quietly doing a lot of work behind the scenes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Helpful learning resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.w3.org/WAI/" rel="noopener noreferrer"&gt;W3C WAI accessibility guidance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/company/w3schools-com" rel="noopener noreferrer"&gt;W3Schools accessibility tutorials&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.mozilla.org/" rel="noopener noreferrer"&gt;MDN Web Docs&lt;/a&gt;
Originally published at &lt;a href="https://blog.mspk.me/posts/the-real-cost-of-css-variables-runtime-resolution-and-performance-implications" rel="noopener noreferrer"&gt;Under The Hood&lt;/a&gt;.
Get the next deep dive in your inbox: &lt;a href="https://blog.mspk.me/#subscribe" rel="noopener noreferrer"&gt;subscribe to Under The Hood&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>css</category>
      <category>performance</category>
      <category>frontend</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How CSS Containment Affects Layout and Paint Performance in Large Apps</title>
      <dc:creator>Srikar Phani Kumar Marti</dc:creator>
      <pubDate>Mon, 19 Oct 2026 10:46:11 +0000</pubDate>
      <link>https://dev.to/mspk97/how-css-containment-affects-layout-and-paint-performance-in-large-apps-162l</link>
      <guid>https://dev.to/mspk97/how-css-containment-affects-layout-and-paint-performance-in-large-apps-162l</guid>
      <description>&lt;p&gt;Ever been frustrated by a slow, janky UI in your large app, and wondered if there’s some CSS magic to speed things up? You might have stumbled upon the CSS &lt;code&gt;contain&lt;/code&gt; property and thought, "Great, I’ll just slap this on some big container, and the browser will do less work, right?"&lt;/p&gt;

&lt;p&gt;I fell into that trap myself. I added &lt;code&gt;contain: layout paint;&lt;/code&gt; to a few big sections and felt an initial rush, my app felt snappier. But then, weird bugs started showing up. Some parts didn’t repaint right, elements overflowed unexpectedly, and animations broke. What gives?&lt;/p&gt;

&lt;p&gt;Let’s get under the hood and see exactly what CSS containment does, why it matters for layout and paint performance, and how to avoid those frustrating surprises.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is CSS Containment, Really?
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;contain&lt;/code&gt; property tells the browser to isolate a DOM subtree from the rest of the page in certain ways. Think of it as declaring a "boundary" for browser work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;layout containment&lt;/strong&gt; (&lt;code&gt;contain: layout&lt;/code&gt;): The browser can treat the element and its descendants as independent for layout calculations. Changes inside won’t force the browser to re-layout everything outside.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;paint containment&lt;/strong&gt; (&lt;code&gt;contain: paint&lt;/code&gt;): The browser knows the element's rendering is self-contained and won't paint outside its bounds. This can improve paint performance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;size containment&lt;/strong&gt; (&lt;code&gt;contain: size&lt;/code&gt;): The element’s size is independent; the browser doesn’t check children to compute size.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can combine these: &lt;code&gt;contain: layout paint;&lt;/code&gt; or &lt;code&gt;contain: strict;&lt;/code&gt; (which includes layout, paint, size, style, and more).&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does This Help Performance?
&lt;/h2&gt;

&lt;p&gt;Browsers spend a lot of time recalculating layout and repainting when something changes, like DOM updates, style changes, or animations. Normally, these changes can ripple through the entire page, causing a cascade of work.&lt;/p&gt;

&lt;p&gt;When you set containment, you tell the browser: "Hey, this subtree won’t affect anything outside." That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Layout changes inside don’t cause re-layout outside.&lt;/li&gt;
&lt;li&gt;Paint work can be clipped and cached more aggressively.&lt;/li&gt;
&lt;li&gt;The browser can optimize invalidation regions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In large apps with complex component trees, this isolation can dramatically reduce the work browsers do per frame, keeping your UI smooth.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Real-World Debugging Tale
&lt;/h2&gt;

&lt;p&gt;I added &lt;code&gt;contain: layout paint;&lt;/code&gt; to a sidebar component in a React app. The sidebar is a big, complex tree that updates frequently.&lt;/p&gt;

&lt;p&gt;At first, everything was smoother. Typing in inputs inside the sidebar didn’t freeze the main content. Great!&lt;/p&gt;

&lt;p&gt;But then, some dropdown menus inside the sidebar started rendering outside their bounds, overflowing or getting clipped. Animations inside the sidebar also sometimes jumped or flickered.&lt;/p&gt;

&lt;p&gt;What happened?&lt;/p&gt;

&lt;p&gt;Turns out, &lt;code&gt;contain: paint&lt;/code&gt; creates a new stacking context and clipping boundary. If your dropdowns use &lt;code&gt;position: fixed&lt;/code&gt; or rely on overflow visible outside the container, containment clips them off. The browser assumes the subtree can't paint outside itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Breaking Down the Tradeoffs
&lt;/h2&gt;

&lt;p&gt;Here’s what you gain and lose when you use &lt;code&gt;contain&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gain:&lt;/strong&gt; Faster layout and paint because the browser scopes work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lose:&lt;/strong&gt; Elements inside can’t affect outside layout or paint. This breaks things like:

&lt;ul&gt;
&lt;li&gt;Overflow visible elements (tooltips, dropdowns)&lt;/li&gt;
&lt;li&gt;CSS filters and blend modes spilling outside&lt;/li&gt;
&lt;li&gt;Some CSS inheritance behaviors&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How Browsers Implement Containment
&lt;/h2&gt;

&lt;p&gt;Under the hood, containment lets browsers do incremental work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They calculate layout for the contained subtree independently.&lt;/li&gt;
&lt;li&gt;They create separate layers and stacking contexts for paint.&lt;/li&gt;
&lt;li&gt;They limit invalidation regions to those layers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This isolation reduces the cost of style recalculation, layout, and paint, especially when you have lots of DOM nodes.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Use &lt;code&gt;contain&lt;/code&gt; in Production Apps
&lt;/h2&gt;

&lt;p&gt;Start with these guidelines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use on large container elements with complex subtrees that update often.&lt;/li&gt;
&lt;li&gt;Avoid on elements with children that rely on overflow visible or fixed positioning that escapes their bounds.&lt;/li&gt;
&lt;li&gt;Test carefully in browsers you support. Containment behavior can vary slightly.&lt;/li&gt;
&lt;li&gt;Combine with layout debugging tools (like Chrome’s Paint Flashing) to see how containment affects repainting.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Debugging Unexpected Behavior
&lt;/h2&gt;

&lt;p&gt;If you see clipped dropdowns, tooltips, or animations flickering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check if &lt;code&gt;contain&lt;/code&gt; is set on any ancestor.&lt;/li&gt;
&lt;li&gt;Try removing &lt;code&gt;paint&lt;/code&gt; containment first; &lt;code&gt;layout&lt;/code&gt; alone is less restrictive.&lt;/li&gt;
&lt;li&gt;Inspect stacking contexts (&lt;code&gt;element.style.contain&lt;/code&gt;) and overflow properties.&lt;/li&gt;
&lt;li&gt;Consider moving popups outside contained containers using portals or separate DOM nodes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Example: Containment and Dropdowns
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.sidebar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;contain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;layout&lt;/span&gt; &lt;span class="n"&gt;paint&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.dropdown&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c"&gt;/* But if .sidebar has paint containment, the dropdown might be clipped! */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;.sidebar&lt;/code&gt; uses &lt;code&gt;contain: paint&lt;/code&gt;, the dropdown can’t paint outside &lt;code&gt;.sidebar&lt;/code&gt;'s bounding box, causing visual clipping.&lt;/p&gt;

&lt;p&gt;Workaround: Render dropdowns at a higher DOM level (e.g., React portals) so they escape the containment boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Quick Checklist Before Adding &lt;code&gt;contain&lt;/code&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Does the container have children that overflow outside or use fixed positioning?&lt;/li&gt;
&lt;li&gt;Will descendants use CSS filters, shadows, or blend modes that extend beyond boundaries?&lt;/li&gt;
&lt;li&gt;Are you okay with the contained subtree being isolated from the rest of the page’s style and layout?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the answer is no to any, containment might cause subtle bugs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;CSS containment is a powerful tool to improve performance by letting browsers scope layout and paint work. But it’s not a silver bullet. Knowing what it does under the hood helps you make informed tradeoffs and avoid bugs that can feel like ghost problems.&lt;/p&gt;

&lt;p&gt;Next time your large app stutters or repaints too much, try containment, but keep an eye on those sneaky clipping and stacking bugs. And remember, sometimes the best fix is a well-placed React portal or layering tweak.&lt;/p&gt;

&lt;p&gt;Give it a try, and you’ll start seeing your app from the browser’s eyes, focused, isolated, and faster.&lt;/p&gt;




&lt;h2&gt;
  
  
  Helpful learning resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/" rel="noopener noreferrer"&gt;MDN Web Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web.dev/" rel="noopener noreferrer"&gt;web.dev performance guidance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://react.dev/" rel="noopener noreferrer"&gt;React documentation&lt;/a&gt;
Originally published at &lt;a href="https://blog.mspk.me/posts/how-css-containment-affects-layout-and-paint-performance-in-large-apps" rel="noopener noreferrer"&gt;Under The Hood&lt;/a&gt;.
Get the next deep dive in your inbox: &lt;a href="https://blog.mspk.me/#subscribe" rel="noopener noreferrer"&gt;subscribe to Under The Hood&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>css</category>
      <category>performance</category>
      <category>frontend</category>
      <category>rendering</category>
    </item>
    <item>
      <title>What’s New and Different in Angular NgRX Store Version 22</title>
      <dc:creator>Srikar Phani Kumar Marti</dc:creator>
      <pubDate>Fri, 16 Oct 2026 11:35:32 +0000</pubDate>
      <link>https://dev.to/mspk97/whats-new-and-different-in-angular-ngrx-store-version-22-2ll7</link>
      <guid>https://dev.to/mspk97/whats-new-and-different-in-angular-ngrx-store-version-22-2ll7</guid>
      <description>&lt;p&gt;You know that feeling when you upgrade a familiar library ,  and suddenly your app behaves slightly differently? No errors, no big crashes, just subtle changes that make you scratch your head?&lt;/p&gt;

&lt;p&gt;That’s exactly what happened when I upgraded an Angular app from NgRX 13 to NgRX 22 recently. I rely on NgRX store for state management and effects, but version 22 introduced some tweaks that changed how selectors behave, typing improves, and runtime performance shifts.&lt;/p&gt;

&lt;p&gt;Here’s a walkthrough of the key changes I encountered, what’s really going on under the hood, and how you can spot and adapt to these differences.&lt;/p&gt;

&lt;h2&gt;
  
  
  The NgRX store you thought you knew
&lt;/h2&gt;

&lt;p&gt;If you’ve used NgRX store before, you know it’s basically Redux for Angular, with selectors, actions, reducers, and effects to manage state in a reactive way.&lt;/p&gt;

&lt;p&gt;The core concepts stayed the same in v22, but the implementation details and typings improved. Still, some subtle differences sneaked in that can trip you up.&lt;/p&gt;

&lt;p&gt;For example, the way selectors memoize results and how they handle parameterized selectors got some internal revisions. Also, there’s better TypeScript inference for feature selectors now.&lt;/p&gt;

&lt;h2&gt;
  
  
  What caught me off guard: Selector memoization changes
&lt;/h2&gt;

&lt;p&gt;In previous versions, selector memoization was straightforward: a selector caches its last input and output, and skips recalculations if inputs are strictly equal.&lt;/p&gt;

&lt;p&gt;With NgRX 22, the selector memoization logic was refined to improve performance and avoid stale data. It now better tracks dependencies internally and avoids unnecessary recalculations in some edge cases.&lt;/p&gt;

&lt;p&gt;Sounds great until you hit this weird bug where a selector that depended on a nested object stopped updating when the nested object’s properties changed but the reference didn’t.&lt;/p&gt;

&lt;p&gt;Here’s a quick example:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;selectFeature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createFeatureSelector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MyFeatureState&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;feature&lt;/span&gt;&lt;span class="dl"&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;selectNestedValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;selectFeature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;feature&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nested&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your reducer mutated &lt;code&gt;nested&lt;/code&gt; in place (which you shouldn’t do, but sometimes happens), the selector might not re-run because the top-level feature object reference didn’t change.&lt;/p&gt;

&lt;p&gt;NgRX 22 memoizes more aggressively and relies on inputs’ references for change detection more strictly. This pushed me to audit reducers and ensure immutable updates everywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  Better typing for feature selectors
&lt;/h2&gt;

&lt;p&gt;One of the biggest quality-of-life improvements in NgRX 22 is how TypeScript infers types for feature selectors.&lt;/p&gt;

&lt;p&gt;Previously, you had to manually specify state interfaces frequently, or deal with loose typings that made autocomplete and error detection weaker.&lt;/p&gt;

&lt;p&gt;Now, &lt;code&gt;createFeatureSelector&lt;/code&gt; fully infers the feature slice type if you pass a generic argument, and downstream selectors get tighter typings automatically. This made refactoring safer and helped catch bugs early in my IDE.&lt;/p&gt;

&lt;p&gt;Here’s how I updated selectors:&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="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;MyFeatureState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nl"&gt;loading&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;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;selectFeature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createFeatureSelector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MyFeatureState&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;myFeature&lt;/span&gt;&lt;span class="dl"&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;selectItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;selectFeature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With version 22, the &lt;code&gt;state&lt;/code&gt; argument in the selector callback is correctly typed as &lt;code&gt;MyFeatureState&lt;/code&gt;, so no more casting or guessing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Breaking changes to note: StoreModule.forRoot and forFeature
&lt;/h2&gt;

&lt;p&gt;The NgRX team cleaned up the StoreModule APIs slightly. While the old syntax still works, it now warns if you mix incompatible configurations.&lt;/p&gt;

&lt;p&gt;If your app registers feature states dynamically or with lazy-loaded modules, make sure your &lt;code&gt;forFeature&lt;/code&gt; calls match the expected typings and that you pass reducers directly or as a map object.&lt;/p&gt;

&lt;p&gt;They also recommend using the new &lt;code&gt;provideState()&lt;/code&gt; and &lt;code&gt;provideStore()&lt;/code&gt; APIs introduced alongside v22 for better tree-shaking and standalone API support in Angular 16+. Migrating to these new providers can improve bundle sizes but needs some code changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Effects improvements and subtle runtime behavior changes
&lt;/h2&gt;

&lt;p&gt;NgRX Effects also got some polish. The main change I noticed was around error handling and completion behavior:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Effects don’t complete by default anymore, which means they keep listening indefinitely unless explicitly completed.&lt;/li&gt;
&lt;li&gt;The new &lt;code&gt;createEffect&lt;/code&gt; options let you specify if an effect should dispatch actions or not more clearly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This helped me catch some subtle bugs where effects stopped firing after certain errors or completions in older versions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why these changes matter: A real debugging story
&lt;/h2&gt;

&lt;p&gt;I spent a frustrating afternoon debugging an issue where a selector stopped updating after a certain action dispatched. The UI stayed stale.&lt;/p&gt;

&lt;p&gt;Turns out, the reducer was mutating nested state properties instead of returning new objects. NgRX 22’s stricter memoization ignored the change because the feature slice’s root reference didn’t change.&lt;/p&gt;

&lt;p&gt;Fixing the reducer to return new objects fixed the selector updates immediately:&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;// Bad (mutating nested state)&lt;/span&gt;
&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nested&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Good (immutable update)&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="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;nested&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="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nested&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newValue&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 reinforced the immutable update pattern more strictly than before.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up the NgRX 22 upgrade
&lt;/h2&gt;

&lt;p&gt;Upgrading to NgRX 22 was mostly smooth, but it nudged me to pay closer attention to immutability and typing.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Selector memoization is more aggressive and reference-based.&lt;/li&gt;
&lt;li&gt;Typings for feature selectors got much better.&lt;/li&gt;
&lt;li&gt;StoreModule APIs have new providers and recommended usage for Angular standalone.&lt;/li&gt;
&lt;li&gt;Effects behavior around completion and dispatch is clearer now.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re upgrading, audit your reducers for immutable patterns, update your selectors to use new typings, and consider switching to the new provider APIs.&lt;/p&gt;

&lt;p&gt;NgRX store is still the solid choice for Angular state management, but version 22 reminds you to keep your state immutable and your typings tight.&lt;/p&gt;

&lt;p&gt;If you’ve run into other quirks or hidden gems in NgRX 22, I’d love to hear about them!&lt;/p&gt;




&lt;h2&gt;
  
  
  Helpful learning resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/" rel="noopener noreferrer"&gt;MDN Web Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web.dev/" rel="noopener noreferrer"&gt;web.dev performance guidance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://react.dev/" rel="noopener noreferrer"&gt;React documentation&lt;/a&gt;
Originally published at &lt;a href="https://blog.mspk.me/posts/what-s-new-and-different-in-angular-ngrx-store-version-22" rel="noopener noreferrer"&gt;Under The Hood&lt;/a&gt;.
Get the next deep dive in your inbox: &lt;a href="https://blog.mspk.me/#subscribe" rel="noopener noreferrer"&gt;subscribe to Under The Hood&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>angular</category>
      <category>ngrx</category>
      <category>statemanagement</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Understanding Paint Containment and Composite Layers for Smooth Animations</title>
      <dc:creator>Srikar Phani Kumar Marti</dc:creator>
      <pubDate>Thu, 15 Oct 2026 14:08:40 +0000</pubDate>
      <link>https://dev.to/mspk97/understanding-paint-containment-and-composite-layers-for-smooth-animations-2f04</link>
      <guid>https://dev.to/mspk97/understanding-paint-containment-and-composite-layers-for-smooth-animations-2f04</guid>
      <description>&lt;p&gt;You’re building a slick UI with smooth animations. You add a subtle transform here, a fade there. But then, boom ,  your animations stutter. Frames drop, interactions feel sluggish, and your user experience tanks.&lt;/p&gt;

&lt;p&gt;You tweak your CSS, add &lt;code&gt;will-change: transform&lt;/code&gt;, and suddenly things get smoother. But why? What actually happens under the hood when you add that mysterious CSS property? Or when you add &lt;code&gt;contain: paint&lt;/code&gt;? How do those tiny CSS hints help the browser decide to offload rendering work to the GPU?&lt;/p&gt;

&lt;p&gt;Let me share what I learned digging into paint containment and compositing layers ,  the quiet heroes behind buttery smooth animations and frustrating jank.&lt;/p&gt;

&lt;h2&gt;
  
  
  The moment you realize something’s wrong
&lt;/h2&gt;

&lt;p&gt;I was working on a dashboard with complex nested components and animations. Some animations were jittery, especially when scrolling or interacting with other UI parts.&lt;/p&gt;

&lt;p&gt;Opening Chrome DevTools, I checked the Performance tab and noticed a lot of layout and paint work every frame. The GPU wasn’t getting a break.&lt;/p&gt;

&lt;p&gt;I suspected compositing layers weren’t being created smartly. If each animated element had its own layer, the browser could push rasterization to the GPU, freeing up the CPU and enabling smooth 60fps animations.&lt;/p&gt;

&lt;p&gt;But how does the browser decide what gets its own compositing layer?&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a compositing layer anyway?
&lt;/h2&gt;

&lt;p&gt;Imagine the browser’s rendering pipeline like an assembly line:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Style:&lt;/strong&gt; The browser figures out what styles apply to each element.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Layout:&lt;/strong&gt; Positions and sizes are computed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Paint:&lt;/strong&gt; Elements are drawn into bitmaps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composite:&lt;/strong&gt; These bitmaps (layers) are combined to produce the final image.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Most elements are painted into a shared bitmap. But some elements get their &lt;em&gt;own&lt;/em&gt; bitmap ,  a compositing layer. This bitmap can be handed off to the GPU to be transformed or animated independently without repainting the entire page.&lt;/p&gt;

&lt;p&gt;When a layer is created, animations like transforms or opacity changes on that layer become super cheap ,  the GPU just moves or blends that bitmap.&lt;/p&gt;

&lt;h2&gt;
  
  
  Paint containment: telling the browser to isolate painting
&lt;/h2&gt;

&lt;p&gt;You can hint to the browser when an element should get its own compositing layer by using CSS containment. The &lt;code&gt;contain&lt;/code&gt; property lets you declare what aspects of rendering are confined to a subtree.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;contain: paint&lt;/code&gt; means “hey browser, this element and its subtree won’t affect anything outside their bounding box when painting.”&lt;/p&gt;

&lt;p&gt;Why does this matter? Because if the browser knows an element is paint-contained, it can safely create a compositing layer for it. Changes inside won’t force repaints outside.&lt;/p&gt;

&lt;p&gt;Consider this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;contain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;paint&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;will-change&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;transform&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;Here, &lt;code&gt;.card&lt;/code&gt; is paint-contained and hints it will be transformed. The browser can create a separate layer for &lt;code&gt;.card&lt;/code&gt; and push its rendering work to the GPU.&lt;/p&gt;

&lt;h2&gt;
  
  
  How paint containment creates layers under the hood
&lt;/h2&gt;

&lt;p&gt;Under the hood, the browser’s rendering engine tracks containment properties during the paint phase.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When it hits an element with &lt;code&gt;contain: paint&lt;/code&gt;, it creates a new &lt;em&gt;paint layer&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;This layer is a bitmap snapshot of that subtree.&lt;/li&gt;
&lt;li&gt;Because it’s isolated, the browser knows repainting this layer won’t affect siblings or ancestors.&lt;/li&gt;
&lt;li&gt;The compositor can now handle that layer independently.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a big win for animations.&lt;/p&gt;

&lt;p&gt;If you animate a transform on that element, the browser only needs to update the layer’s position on screen ,  no repaint or re-layout.&lt;/p&gt;

&lt;p&gt;Without containment, the browser might have to repaint a large part of the page every frame.&lt;/p&gt;

&lt;h2&gt;
  
  
  The GPU angle: why compositing layers matter
&lt;/h2&gt;

&lt;p&gt;Compositing layers are the boundary between CPU-bound painting and GPU-accelerated compositing.&lt;/p&gt;

&lt;p&gt;Your browser’s GPU can move, scale, rotate, or fade whole layers very efficiently.&lt;/p&gt;

&lt;p&gt;But creating too many layers is also bad ,  each layer consumes GPU memory and overhead.&lt;/p&gt;

&lt;p&gt;The browser tries to balance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Too few layers:&lt;/strong&gt; More repaint work on CPU, animations jank.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Too many layers:&lt;/strong&gt; GPU memory pressure, possible thrashing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can influence this balance with &lt;code&gt;contain&lt;/code&gt;, &lt;code&gt;will-change&lt;/code&gt;, and other CSS properties.&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging paint containment and layers in DevTools
&lt;/h2&gt;

&lt;p&gt;If you suspect your animations are slow due to missing layers, here’s how to check:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Layers panel in Chrome DevTools:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open DevTools &amp;gt; More Tools &amp;gt; Layers.&lt;/li&gt;
&lt;li&gt;You’ll see a visual map of compositing layers.&lt;/li&gt;
&lt;li&gt;Animated elements should have their own layers.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Rendering tab &amp;gt; Paint flashing:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enable "Paint flashing" to see what parts of the page repaint during interactions.&lt;/li&gt;
&lt;li&gt;If large areas repaint during a transform animation, you lack proper containment.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Performance tab:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Record a profile while animating.&lt;/li&gt;
&lt;li&gt;Look for "Recalculate Style" and "Paint" tasks.&lt;/li&gt;
&lt;li&gt;Excessive painting per frame hints at missing layers.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Practical tips to avoid jank
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;contain: paint&lt;/code&gt; or &lt;code&gt;contain: layout paint&lt;/code&gt; on animated elements or their parents when possible.&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;will-change: transform&lt;/code&gt; (or whatever property you animate) to hint the browser early.&lt;/li&gt;
&lt;li&gt;Avoid animating properties that force layout or paint (like &lt;code&gt;width&lt;/code&gt;, &lt;code&gt;height&lt;/code&gt;, &lt;code&gt;top&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Keep your layer tree manageable: don’t paint-contain everything blindly.&lt;/li&gt;
&lt;li&gt;Test on real devices with DevTools profiling.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Real-world gotchas
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Not all browsers support all containment types equally.&lt;/li&gt;
&lt;li&gt;Overusing &lt;code&gt;will-change&lt;/code&gt; can backfire and cause memory bloat.&lt;/li&gt;
&lt;li&gt;Complex shadow DOM or web components might not behave as expected without explicit containment.&lt;/li&gt;
&lt;li&gt;Sometimes, a parent’s overflow or opacity settings affect layer creation unexpectedly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why understanding this matters
&lt;/h2&gt;

&lt;p&gt;When you grasp how paint containment and compositing layers work, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write CSS that helps the browser optimize rendering.&lt;/li&gt;
&lt;li&gt;Debug tricky animation jank by spotting missing layers.&lt;/li&gt;
&lt;li&gt;Balance GPU memory usage and CPU load.&lt;/li&gt;
&lt;li&gt;Build smoother, more responsive UIs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next time your animation stutters, pause and inspect your layers. Your GPU will thank you.&lt;/p&gt;




&lt;h2&gt;
  
  
  Helpful learning resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/" rel="noopener noreferrer"&gt;MDN Web Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web.dev/" rel="noopener noreferrer"&gt;web.dev performance guidance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://platform.openai.com/docs" rel="noopener noreferrer"&gt;OpenAI developer resources&lt;/a&gt;
Originally published at &lt;a href="https://blog.mspk.me/posts/understanding-paint-containment-and-composite-layers-for-smooth-animations" rel="noopener noreferrer"&gt;Under The Hood&lt;/a&gt;.
Get the next deep dive in your inbox: &lt;a href="https://blog.mspk.me/#subscribe" rel="noopener noreferrer"&gt;subscribe to Under The Hood&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>css</category>
      <category>performance</category>
      <category>animation</category>
      <category>rendering</category>
    </item>
    <item>
      <title>How Angular Signals Really Work: Behind the Scenes of Reactivity</title>
      <dc:creator>Srikar Phani Kumar Marti</dc:creator>
      <pubDate>Tue, 13 Oct 2026 10:49:13 +0000</pubDate>
      <link>https://dev.to/mspk97/how-angular-signals-really-work-behind-the-scenes-of-reactivity-38h5</link>
      <guid>https://dev.to/mspk97/how-angular-signals-really-work-behind-the-scenes-of-reactivity-38h5</guid>
      <description>&lt;p&gt;You’ve been there: you update some Angular state, expecting your UI to refresh instantly. But the component stays stubbornly frozen, no errors or warnings ,  just silence. Maybe you’re using Angular Signals, and they promised to simplify reactivity, but your updates don’t always reflect where you expect.&lt;/p&gt;

&lt;p&gt;That exact thing happened to me recently. At first, I thought it was a bug in my code. After some digging, I realized the mystery was how Angular Signals track dependencies and trigger updates behind the scenes. It’s not magic, but it’s subtle.&lt;/p&gt;

&lt;p&gt;Let me walk you through what I figured out about Angular Signals ,  how they manage state reactively, how they track which parts of your app depend on them, and how they kick off Angular’s change detection when needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The moment a signal feels like magic ,  or doesn’t
&lt;/h2&gt;

&lt;p&gt;Signals in Angular look like reactive variables. You declare a signal with &lt;code&gt;signal(initialValue)&lt;/code&gt; and read or write its &lt;code&gt;.value&lt;/code&gt;. When the value changes, anything depending on it should update.&lt;/p&gt;

&lt;p&gt;Sounds simple, right? But here’s the catch: those "anything depending on it" are tracked, not guessed.&lt;/p&gt;

&lt;p&gt;Imagine this component snippet:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&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="nd"&gt;Component&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CounterComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;double&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computed&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;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&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;If your template uses &lt;code&gt;{{ double() }}&lt;/code&gt;, Angular automatically knows to update when &lt;code&gt;count&lt;/code&gt; changes, because &lt;code&gt;double&lt;/code&gt; depends on it. But what if you just read &lt;code&gt;count.value&lt;/code&gt; directly in some plain function, outside Angular's reactive context? The signal won’t know to notify anyone.&lt;/p&gt;

&lt;p&gt;I ran into this when I had a signal update inside an event handler that wasn’t wrapped properly in Angular’s reactive context. No UI update happened ,  the component didn’t re-render because Angular’s change detection wasn’t triggered.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signals are reactive primitives with dependency tracking
&lt;/h2&gt;

&lt;p&gt;Under the hood, a signal is not just a value holder. It keeps track of who reads its value during a reactive computation.&lt;/p&gt;

&lt;p&gt;Here’s how it works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When you call a computed signal or Angular evaluates a component’s template, Angular creates a "tracking context."&lt;/li&gt;
&lt;li&gt;As the computation runs, every signal’s &lt;code&gt;.value&lt;/code&gt; getter checks if there’s a current tracking context.&lt;/li&gt;
&lt;li&gt;If there is, the signal records that the computation depends on it.&lt;/li&gt;
&lt;li&gt;When the signal’s value changes, it looks up all the computations (or subscribers) that depend on it and notifies them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means Angular knows precisely which parts of your UI rely on which signals, so it only updates what actually needs to change.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Angular signals tie into change detection
&lt;/h2&gt;

&lt;p&gt;Signals themselves don’t update the UI directly. They notify their subscribers, which could be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Computed signals that recalculate derived values&lt;/li&gt;
&lt;li&gt;Angular’s change detection mechanism to re-render components&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When a signal updates, it queues Angular's change detection for the associated component. This happens asynchronously, usually scheduled via &lt;code&gt;NgZone&lt;/code&gt; or microtasks.&lt;/p&gt;

&lt;p&gt;That’s why sometimes you see updates batched together ,  Angular waits for the current synchronous work to finish before running change detection.&lt;/p&gt;

&lt;p&gt;If you update a signal outside Angular’s zone, or in some async callback without zone awareness, Angular won’t know to check your component again. That was the root cause of my silent update bug.&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging signals: What to look for
&lt;/h2&gt;

&lt;p&gt;Here are some concrete things I learned to check when your Angular Signals don’t seem to update the UI:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Are you reading the signal inside a reactive context?&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Reading &lt;code&gt;signal.value&lt;/code&gt; in a plain function without Angular's tracking won’t register a dependency.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Is the signal update happening inside Angular’s zone?&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you update a signal inside a callback from a non-Angular source (like &lt;code&gt;setTimeout&lt;/code&gt; or a third-party library), Angular might miss it. Use &lt;code&gt;NgZone.run()&lt;/code&gt; or &lt;code&gt;zone.runOutsideAngular()&lt;/code&gt; carefully.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Are you using computed signals correctly?&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Computed signals cache their results and only recompute when dependencies change. If your computed signal references something outside signals, it won’t update.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Are you mixing signals with other reactive tools?&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Angular Signals work best when you stick to their reactive model. Mixing RxJS or other state management patterns requires bridging carefully.&lt;/p&gt;

&lt;h2&gt;
  
  
  A simple example to illustrate
&lt;/h2&gt;

&lt;p&gt;Here’s a minimal example I played with:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&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;doubled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computed&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;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&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;my-counter&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;p&amp;gt;Count: {{ count() }}&amp;lt;/p&amp;gt;
    &amp;lt;p&amp;gt;Doubled: {{ doubled() }}&amp;lt;/p&amp;gt;
    &amp;lt;button (click)="increment()"&amp;gt;Increment&amp;lt;/button&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;MyCounter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;doubled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;doubled&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;increment&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;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&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;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&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 works perfectly because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The template calls &lt;code&gt;count()&lt;/code&gt; and &lt;code&gt;doubled()&lt;/code&gt;, so Angular tracks those dependencies.&lt;/li&gt;
&lt;li&gt;Clicking the button calls &lt;code&gt;increment()&lt;/code&gt;, which updates the signal.&lt;/li&gt;
&lt;li&gt;The signal change triggers Angular’s change detection to re-render the template.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But if you read &lt;code&gt;count.value&lt;/code&gt; outside Angular’s reactive system (say in a console log or a raw callback), Angular won’t know to update anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters for your app
&lt;/h2&gt;

&lt;p&gt;Signals are powerful because they give you fine-grained control over reactivity without heavy machinery. But that power comes with responsibility:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You must be mindful of where you access signals&lt;/li&gt;
&lt;li&gt;Understand how Angular tracks dependencies&lt;/li&gt;
&lt;li&gt;Ensure updates happen inside Angular’s zone&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you get it, signals can simplify your state management and reduce unnecessary renders.&lt;/p&gt;

&lt;p&gt;But if you treat them like plain variables, you’ll end up chasing bugs where your UI silently refuses to update.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Angular Signals are more than just reactive variables, they’re reactive primitives with smart dependency tracking and integration with Angular’s change detection. The secret to getting them right? Understanding where and how Angular tracks signal usage and triggers updates.&lt;/p&gt;

&lt;p&gt;Next time your UI feels stuck after a signal update, check if you’re in the right reactive context and Angular’s zone. That little insight saved me hours of debugging.&lt;/p&gt;

&lt;p&gt;Signals aren’t magic, but once you understand how they work under the hood, you’ll find them a practical and elegant way to manage state in Angular.&lt;/p&gt;




&lt;h2&gt;
  
  
  Helpful learning resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.w3.org/WAI/" rel="noopener noreferrer"&gt;W3C WAI accessibility guidance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/company/w3schools-com" rel="noopener noreferrer"&gt;W3Schools accessibility tutorials&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.mozilla.org/" rel="noopener noreferrer"&gt;MDN Web Docs&lt;/a&gt;
Originally published at &lt;a href="https://blog.mspk.me/posts/how-angular-signals-really-work-behind-the-scenes-of-reactivity" rel="noopener noreferrer"&gt;Under The Hood&lt;/a&gt;.
Get the next deep dive in your inbox: &lt;a href="https://blog.mspk.me/#subscribe" rel="noopener noreferrer"&gt;subscribe to Under The Hood&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>angular</category>
      <category>signals</category>
      <category>reactivity</category>
      <category>changedetection</category>
    </item>
    <item>
      <title>Angular Standalone Components: How They Change Compilation and Dependency Injection</title>
      <dc:creator>Srikar Phani Kumar Marti</dc:creator>
      <pubDate>Mon, 12 Oct 2026 11:21:10 +0000</pubDate>
      <link>https://dev.to/mspk97/angular-standalone-components-how-they-change-compilation-and-dependency-injection-2kie</link>
      <guid>https://dev.to/mspk97/angular-standalone-components-how-they-change-compilation-and-dependency-injection-2kie</guid>
      <description>&lt;p&gt;Ever had that nagging feeling when your Angular module files start piling up, and the boilerplate for NgModules feels like a mountain you just want to avoid climbing? I ran into this recently trying to simplify a mid-sized Angular app. Then Angular’s standalone components landed and suddenly, things started to look cleaner, and stranger under the hood.&lt;/p&gt;

&lt;p&gt;Standalone components let you skip NgModules entirely for many cases. That sounds like a purely syntactic win, but it actually shakes up how Angular’s compiler processes components and how dependency injection (DI) scopes get wired together. I dug into what actually changes, and what it means when debugging weird DI errors or compilation issues in large apps.&lt;/p&gt;

&lt;h2&gt;
  
  
  The familiar setup: NgModules as the backbone
&lt;/h2&gt;

&lt;p&gt;For years, Angular developers have leaned on NgModules to group components, directives, and pipes, and crucially, to configure DI scopes and compilation context. When you build or add a component, you register it inside a module’s declarations. Services get provided either at the root or inside modules via &lt;code&gt;providers&lt;/code&gt; arrays.&lt;/p&gt;

&lt;p&gt;The compiler gathers metadata from all these modules to generate factories and link DI providers. It’s a big coordination job. This centralized NgModule system shaped how Angular apps are structured, how lazy loading works, and how scopes of injectors nest.&lt;/p&gt;

&lt;p&gt;But there’s a cost: boilerplate explodes, and sometimes it’s hard to untangle which module provides what, especially in large apps with many feature modules.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter standalone components: no module needed
&lt;/h2&gt;

&lt;p&gt;Standalone components flip this pattern. You declare a component with &lt;code&gt;standalone: true&lt;/code&gt; in its decorator:&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="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-widget&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;templateUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./widget.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;standalone&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="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;CommonModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;FormsModule&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;// just like NgModule imports&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;WidgetComponent&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No NgModule to declare it in. You import needed Angular directives or other components directly via the &lt;code&gt;imports&lt;/code&gt; array on the component itself.&lt;/p&gt;

&lt;p&gt;While this feels like a breath of fresh air, it also means Angular’s compiler has to handle components differently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compilation shifts: from NgModule metadata to component-level context
&lt;/h2&gt;

&lt;p&gt;With NgModules, the compiler collects all declarations and imports at module level, generating a giant compilation graph. Standalone components move this graph closer to the component, bundling its dependencies and metadata tightly.&lt;/p&gt;

&lt;p&gt;This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The compiler generates factories for standalone components that include their own import graphs.&lt;/li&gt;
&lt;li&gt;Tree-shaking gets friendlier, no need to pull in an entire NgModule just because a component depends on one directive inside it.&lt;/li&gt;
&lt;li&gt;The compilation is more incremental and localized.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I noticed this when rebuilding a standalone-heavy app and watching the Angular build logs. The compiler reported different metadata files emitted, and fewer global module summaries. Debugging compilation errors required me to look at component import arrays rather than hunting for the culprit module.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dependency Injection scopes: the subtle but important changes
&lt;/h2&gt;

&lt;p&gt;NgModules have been the usual DI scope boundaries. When you provide a service in a module, all components declared inside share that injector.&lt;/p&gt;

&lt;p&gt;With standalone components, DI scopes become more flexible, and a bit trickier:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Standalone components create their own injector scope if they provide services.&lt;/li&gt;
&lt;li&gt;When a standalone component imports other standalone components, Angular merges their injectors appropriately.&lt;/li&gt;
&lt;li&gt;You can still provide services at the root level or in component providers, but the absence of modules means the injector hierarchy follows the component tree more directly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, if &lt;code&gt;WidgetComponent&lt;/code&gt; provides a service in its &lt;code&gt;providers&lt;/code&gt; array, that service instance is scoped to that component subtree. If you nest that widget inside another standalone component, each instance can have its own injector.&lt;/p&gt;

&lt;p&gt;This can catch you off guard if you expect module-wide singleton services but accidentally scope them too narrowly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical debugging implications
&lt;/h2&gt;

&lt;p&gt;I ran into a couple of tricky debugging moments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Unexpected multiple instances of a service&lt;/strong&gt;: Without modules to unify providers, if you provide a service in multiple standalone components, Angular treats them as separate injectors. Your service state can fragment unexpectedly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Missing dependencies at runtime&lt;/strong&gt;: The compiler verifies &lt;code&gt;imports&lt;/code&gt; on standalone components carefully. Forgetting to import a directive or pipe that was previously imported transitively via a module leads to runtime template errors.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lazy loading changes&lt;/strong&gt;: Lazy-loaded routes can now point directly to standalone components, which changes how injector hierarchies form. Debugging injector trees requires inspecting component injector parents rather than module injectors.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What this means for large apps
&lt;/h2&gt;

&lt;p&gt;For big Angular apps, standalone components can simplify the dependency graph by removing module layers. But you need to be intentional about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Explicit imports on components&lt;/strong&gt;: You no longer rely on deep NgModule import chains. Every component must declare what it needs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Service provider placement&lt;/strong&gt;: Decide carefully where you provide services. If you want a singleton, provide at root or in a core standalone component that wraps the app.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Injector awareness&lt;/strong&gt;: When debugging DI errors, think component injector tree, not module injector tree.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Compilation boundaries&lt;/strong&gt;: Build tooling and compilers now work more incrementally. This can speed up builds but also surface errors earlier.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrapping up: standalone means rethink, not just remove modules
&lt;/h2&gt;

&lt;p&gt;Standalone components in Angular are more than just a shortcut to skip NgModules. They shift compilation from a module-centric graph to a component-centric one and change how dependency injection scopes operate.&lt;/p&gt;

&lt;p&gt;If you try them, prepare to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Explicitly import every dependency in your component&lt;/li&gt;
&lt;li&gt;Rethink service provider scopes&lt;/li&gt;
&lt;li&gt;Learn new debugging mental models focusing on component injector hierarchies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But in return, you get lighter components, more granular compilation, and a flexible DI model that aligns more closely with your UI structure.&lt;/p&gt;

&lt;p&gt;If your Angular app ever felt weighed down by modules, standalone components might just be the fresh start you need. Just remember, under the hood, Angular is doing quite a bit more work to keep your app running smoothly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Helpful learning resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/" rel="noopener noreferrer"&gt;MDN Web Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://platform.openai.com/docs" rel="noopener noreferrer"&gt;OpenAI developer resources&lt;/a&gt;
Originally published at &lt;a href="https://blog.mspk.me/posts/angular-standalone-components-how-they-change-compilation-and-dependency-injection" rel="noopener noreferrer"&gt;Under The Hood&lt;/a&gt;.
Get the next deep dive in your inbox: &lt;a href="https://blog.mspk.me/#subscribe" rel="noopener noreferrer"&gt;subscribe to Under The Hood&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>angular</category>
      <category>standalonecomponents</category>
      <category>dependencyinjection</category>
      <category>compiler</category>
    </item>
    <item>
      <title>Decoding the CSS Cascade: How Browsers Resolve Conflicting Declarations</title>
      <dc:creator>Srikar Phani Kumar Marti</dc:creator>
      <pubDate>Fri, 09 Oct 2026 11:53:29 +0000</pubDate>
      <link>https://dev.to/mspk97/decoding-the-css-cascade-how-browsers-resolve-conflicting-declarations-1464</link>
      <guid>https://dev.to/mspk97/decoding-the-css-cascade-how-browsers-resolve-conflicting-declarations-1464</guid>
      <description>&lt;p&gt;Ever added a CSS rule, sure it should apply, but the browser stubbornly sticks to the old style? You tweak selectors, add &lt;code&gt;!important&lt;/code&gt;, but still something else wins that you didn’t expect. I’ve been there more times than I care to count.&lt;/p&gt;

&lt;p&gt;CSS feels simple at first: write a selector, set a property, watch the change. But once your stylesheets grow, frameworks pile on layers, and browsers juggle conflicting declarations, things get messy. The secret sauce behind it all is the CSS &lt;strong&gt;cascade&lt;/strong&gt; ,  the rules browsers use to decide which style actually applies when multiple declarations fight.&lt;/p&gt;

&lt;p&gt;I recently dug into how the cascade really works and it changed how I debug CSS issues. Along the way, I noticed how SCSS-generated CSS and its nesting can subtly affect cascade outcomes. Here’s what I learned, with examples you can try yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  When CSS surprises you
&lt;/h2&gt;

&lt;p&gt;Imagine this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.button&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="no"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.highlight&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="no"&gt;red&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 your HTML:&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;class=&lt;/span&gt;&lt;span class="s"&gt;"button highlight"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Click me&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;You expect the button text to be red because &lt;code&gt;.highlight&lt;/code&gt; is last and looks more specific, right? But it stays blue. What’s going on?&lt;/p&gt;

&lt;p&gt;Turns out, &lt;code&gt;.button&lt;/code&gt; and &lt;code&gt;.highlight&lt;/code&gt; have the same specificity (both one class), but the cascade also considers &lt;strong&gt;origin&lt;/strong&gt; and &lt;strong&gt;source order&lt;/strong&gt;. The &lt;code&gt;.button&lt;/code&gt; rule might come from a stylesheet loaded later, or even from a user agent stylesheet or browser extension.&lt;/p&gt;

&lt;p&gt;With SCSS, you might write nested selectors that compile to different source orders than you expect, which can change which rule wins.&lt;/p&gt;

&lt;p&gt;Let’s break down what’s actually happening.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cascade’s main players
&lt;/h2&gt;

&lt;p&gt;The cascade resolves conflicts between multiple CSS declarations targeting the same element and property. It prioritizes declarations based on four factors:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Origin&lt;/strong&gt; ,  Where the CSS comes from (user agent, user, author, inline).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Importance&lt;/strong&gt; ,  Whether a declaration is marked &lt;code&gt;!important&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Specificity&lt;/strong&gt; ,  How precisely a selector targets an element.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Source order&lt;/strong&gt; ,  Which declaration appears last in the CSS when everything else ties.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We’ll walk through each with examples ,  including how SCSS can affect source order.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Origin: Browser vs you
&lt;/h2&gt;

&lt;p&gt;CSS rules come from different origins:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;User agent stylesheets&lt;/strong&gt; ,  The browser’s default styles (like gray buttons).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User stylesheets&lt;/strong&gt; ,  Styles a user applies to override websites (rare for devs).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Author stylesheets&lt;/strong&gt; ,  Your CSS files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inline styles&lt;/strong&gt; ,  Styles written directly on elements via the &lt;code&gt;style&lt;/code&gt; attribute.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The cascade ranks origins roughly like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;User&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nt"&gt;important&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;Author&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nt"&gt;important&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;Author&lt;/span&gt; &lt;span class="nt"&gt;normal&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;User&lt;/span&gt; &lt;span class="nt"&gt;normal&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;User&lt;/span&gt; &lt;span class="nt"&gt;agent&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inline styles have very high specificity and usually win unless overridden by &lt;code&gt;!important&lt;/code&gt; from author or user styles.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&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;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"btn"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"color: green"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Save&lt;span class="nt"&gt;&amp;lt;/button&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="nc"&gt;.btn&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="no"&gt;red&lt;/span&gt; &lt;span class="cp"&gt;!important&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;Here, the &lt;code&gt;.btn&lt;/code&gt; rule is an author &lt;code&gt;!important&lt;/code&gt; style, which beats inline styles without &lt;code&gt;!important&lt;/code&gt;. So the text will be red, not green.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Importance: The &lt;code&gt;!important&lt;/code&gt; trump card
&lt;/h2&gt;

&lt;p&gt;Adding &lt;code&gt;!important&lt;/code&gt; bumps a declaration above normal ones, even with lower specificity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.button&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="no"&gt;blue&lt;/span&gt; &lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.highlight&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="no"&gt;red&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;Now &lt;code&gt;.button&lt;/code&gt; wins because of &lt;code&gt;!important&lt;/code&gt;, even though &lt;code&gt;.highlight&lt;/code&gt; comes later.&lt;/p&gt;

&lt;p&gt;Be careful: overusing &lt;code&gt;!important&lt;/code&gt; turns your CSS into a debugging nightmare. Use it sparingly.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Specificity: Counting your selector’s weight
&lt;/h2&gt;

&lt;p&gt;Specificity measures how precisely a selector targets an element. It’s roughly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Inline styles: 1,0,0,0&lt;/li&gt;
&lt;li&gt;IDs: 0,1,0,0 per ID&lt;/li&gt;
&lt;li&gt;Classes, attributes, pseudo-classes: 0,0,1,0 each&lt;/li&gt;
&lt;li&gt;Element names and pseudo-elements: 0,0,0,1 each&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nf"&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="no"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.nav&lt;/span&gt; &lt;span class="nt"&gt;a&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="no"&gt;red&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;&lt;code&gt;#nav a.active&lt;/code&gt; is more specific because it has an ID selector, so it wins.&lt;/p&gt;

&lt;h2&gt;
  
  
  How SCSS nesting can affect specificity and source order
&lt;/h2&gt;

&lt;p&gt;SCSS lets you nest selectors, which compiles to multiple selectors in CSS. For example:&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="nc"&gt;.button&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="no"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;.highlight&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="no"&gt;red&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;Compiles to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.button&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="no"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.button.highlight&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="no"&gt;red&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;Here, &lt;code&gt;.button.highlight&lt;/code&gt; is more specific than &lt;code&gt;.button&lt;/code&gt; alone, so it wins.&lt;/p&gt;

&lt;p&gt;But if you write:&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="nc"&gt;.highlight&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="no"&gt;red&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.button&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="no"&gt;blue&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 compiled CSS source order has &lt;code&gt;.highlight&lt;/code&gt; first, &lt;code&gt;.button&lt;/code&gt; later ,  so &lt;code&gt;.button&lt;/code&gt; wins if specificity ties.&lt;/p&gt;

&lt;p&gt;SCSS source order depends on how you organize your files and nesting, which affects cascade.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Source order: Last rule wins
&lt;/h2&gt;

&lt;p&gt;When origin, importance, and specificity tie, the declaration that appears last in the CSS wins.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.button&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="no"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.button&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="no"&gt;red&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 button text will be red because the second rule comes later.&lt;/p&gt;

&lt;p&gt;With SCSS, remember that nesting and imports can reorder rules unexpectedly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why specificity can be tricky
&lt;/h2&gt;

&lt;p&gt;Sometimes inline styles override CSS rules with &lt;code&gt;!important&lt;/code&gt;. That’s because inline styles have very high specificity, but &lt;code&gt;!important&lt;/code&gt; can trump inline styles if the &lt;code&gt;!important&lt;/code&gt; comes from the author or user origin.&lt;/p&gt;

&lt;p&gt;Also, attribute selectors and pseudo-classes add to specificity, so adding &lt;code&gt;[type="button"]&lt;/code&gt; or &lt;code&gt;:hover&lt;/code&gt; can change which rule wins.&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging cascade issues: Tools and tricks
&lt;/h2&gt;

&lt;p&gt;When a style won’t apply:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use your browser’s devtools &lt;strong&gt;Computed Styles&lt;/strong&gt; pane to see which rules apply and which get overridden.&lt;/li&gt;
&lt;li&gt;Look for &lt;strong&gt;specificity&lt;/strong&gt; and &lt;strong&gt;origin&lt;/strong&gt; badges.&lt;/li&gt;
&lt;li&gt;Check for &lt;code&gt;!important&lt;/code&gt; flags ,  they usually explain why a rule is winning.&lt;/li&gt;
&lt;li&gt;Review &lt;strong&gt;source order&lt;/strong&gt; in your CSS files and how SCSS compiles your styles.&lt;/li&gt;
&lt;li&gt;Inline styles and JavaScript-injected styles often cause surprises.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Real-world example: Unexpected background color
&lt;/h2&gt;

&lt;p&gt;I once had a card component whose background wouldn’t change despite multiple CSS attempts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.highlighted&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;yellow&lt;/span&gt; &lt;span class="cp"&gt;!important&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;Applied like this:&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;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card highlighted"&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;p&gt;But the background stayed white. Devtools showed an inline style:&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;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card highlighted"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"background-color: white;"&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;p&gt;Inline styles have higher specificity than author styles. The &lt;code&gt;!important&lt;/code&gt; on &lt;code&gt;.highlighted&lt;/code&gt; didn’t override the inline style because the inline style wasn’t marked &lt;code&gt;!important&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The fix? Remove the inline style or add &lt;code&gt;!important&lt;/code&gt; via JavaScript if needed. Better yet, avoid inline styles for such properties.&lt;/p&gt;

&lt;h2&gt;
  
  
  What about inheritance?
&lt;/h2&gt;

&lt;p&gt;Some CSS properties inherit by default (like &lt;code&gt;color&lt;/code&gt; and &lt;code&gt;font-family&lt;/code&gt;). Others don’t (like &lt;code&gt;margin&lt;/code&gt; and &lt;code&gt;border&lt;/code&gt;). The cascade applies after inheritance, so if you see unexpected styles, inheritance might be involved. But inheritance only fills in when no declaration exists ,  it doesn’t override declarations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up the cascade puzzle
&lt;/h2&gt;

&lt;p&gt;The cascade isn’t just "last rule wins." It’s a layered system balancing where styles come from, how important they are, how specific selectors are, and where they appear in your CSS.&lt;/p&gt;

&lt;p&gt;Understanding these layers helps you debug tricky styling issues faster and write more predictable CSS. When that style refuses to apply, don’t just add &lt;code&gt;!important&lt;/code&gt;; pause, check origins, specificity, and source order ,  including how your SCSS compiles ,  and your fix will land smoother.&lt;/p&gt;

&lt;p&gt;Next time your CSS fights back, remember the cascade’s secret hierarchy and use it wisely.&lt;/p&gt;




&lt;h2&gt;
  
  
  Helpful learning resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/" rel="noopener noreferrer"&gt;MDN Web Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://platform.openai.com/docs" rel="noopener noreferrer"&gt;OpenAI developer resources&lt;/a&gt;
Originally published at &lt;a href="https://blog.mspk.me/posts/decoding-the-css-cascade-how-browsers-resolve-conflicting-declarations" rel="noopener noreferrer"&gt;Under The Hood&lt;/a&gt;.
Get the next deep dive in your inbox: &lt;a href="https://blog.mspk.me/#subscribe" rel="noopener noreferrer"&gt;subscribe to Under The Hood&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>css</category>
      <category>frontend</category>
      <category>webdev</category>
      <category>debugging</category>
    </item>
    <item>
      <title>How Angular Signals Work Under the Hood: Reactive Primitives and Change Detection</title>
      <dc:creator>Srikar Phani Kumar Marti</dc:creator>
      <pubDate>Wed, 07 Oct 2026 11:00:47 +0000</pubDate>
      <link>https://dev.to/mspk97/how-angular-signals-work-under-the-hood-reactive-primitives-and-change-detection-d</link>
      <guid>https://dev.to/mspk97/how-angular-signals-work-under-the-hood-reactive-primitives-and-change-detection-d</guid>
      <description>&lt;p&gt;Ever had a component in Angular that stubbornly refuses to update when you think it should? You tweak some state, and the UI looks frozen ,  no error, no warning, just silence. Then, after some trial and error, you realize the problem isn’t your code but how Angular’s new Signals system tracks dependencies and triggers change detection.&lt;/p&gt;

&lt;p&gt;I hit this wall recently while migrating a complex form to use Angular Signals. At first glance, signals seemed like magic ,  reactive primitives that promised simpler state management and automatic updates. But the behavior wasn’t always intuitive, especially when combined with Angular’s existing change detection mechanism.&lt;/p&gt;

&lt;p&gt;Here’s what I learned about how Angular Signals work under the hood, how they track dependencies, how they trigger change detection, and some debugging strategies to tame reactive state bugs in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Angular Signals: More than just reactive variables
&lt;/h2&gt;

&lt;p&gt;Signals in Angular are reactive containers that hold a value and notify subscribers when that value changes. Unlike simple &lt;code&gt;BehaviorSubject&lt;/code&gt;s or &lt;code&gt;EventEmitter&lt;/code&gt;s, signals automatically track which computations depend on them, so updates propagate precisely where needed.&lt;/p&gt;

&lt;p&gt;But how does Angular know which component or template should update when a signal changes? The answer lies in dependency tracking combined with Angular’s change detection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dependency tracking: Catching who uses what
&lt;/h2&gt;

&lt;p&gt;When you read a signal's value inside a reactive computation or template, Angular records that this computation depends on the signal. This happens through a global tracking context:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When Angular evaluates a reactive function (like a computed signal or a component context during change detection), it sets a current "tracking frame."&lt;/li&gt;
&lt;li&gt;When you access a signal’s value inside that frame, the signal registers itself as a dependency.&lt;/li&gt;
&lt;li&gt;Later, when the signal’s value changes, Angular knows exactly which computations or components need to be re-run or marked dirty.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&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;doubled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;doubled&lt;/code&gt; tracks &lt;code&gt;count&lt;/code&gt; as a dependency. When &lt;code&gt;count&lt;/code&gt; updates, Angular will re-run the &lt;code&gt;doubled&lt;/code&gt; computation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signals and Angular change detection: The handshake
&lt;/h2&gt;

&lt;p&gt;Angular traditionally uses zones and dirty checking to detect changes. Signals add a layer of precision by marking affected components as dirty immediately when a signal changes.&lt;/p&gt;

&lt;p&gt;Here’s the flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A signal’s setter updates its value.&lt;/li&gt;
&lt;li&gt;The signal notifies all subscribed computations and Angular components that depend on it.&lt;/li&gt;
&lt;li&gt;Angular marks those components as dirty.&lt;/li&gt;
&lt;li&gt;At the next change detection cycle, only the dirty components re-render.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This reduces unnecessary re-renders and improves performance.&lt;/p&gt;

&lt;p&gt;But it also means if you update a signal outside Angular’s zone or without proper subscription, Angular might not know to check for updates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where bugs creep in: Missing subscriptions and outside zone updates
&lt;/h2&gt;

&lt;p&gt;A frequent gotcha I faced was updating signals in contexts Angular doesn’t track, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Event handlers not wrapped in Angular’s zone.&lt;/li&gt;
&lt;li&gt;Observables or promises updating signals asynchronously.&lt;/li&gt;
&lt;li&gt;Signals used inside third-party libraries or outside Angular’s component context.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In these cases, Angular’s change detection can miss the signal update, leaving the UI stale.&lt;/p&gt;

&lt;p&gt;For instance:&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;runOutsideAngular&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="nf"&gt;runOutsideAngular&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="nf"&gt;setTimeout&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="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Angular may not detect this change&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;1000&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;Without wrapping this in &lt;code&gt;NgZone.run()&lt;/code&gt;, Angular won’t know to run change detection after &lt;code&gt;count&lt;/code&gt; updates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging reactive state bugs: What to watch for
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Check where you update signals:&lt;/strong&gt; Are updates happening inside Angular’s zone? If not, wrap them in &lt;code&gt;NgZone.run()&lt;/code&gt; to ensure change detection triggers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Verify signal subscriptions:&lt;/strong&gt; Components automatically subscribe to signals accessed in templates or computations. But if you read signals imperatively without access during change detection, Angular won’t track dependencies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use &lt;code&gt;effect()&lt;/code&gt; for side effects:&lt;/strong&gt; When you need to run code in response to signal changes, use &lt;code&gt;effect()&lt;/code&gt;. It automatically tracks dependencies and runs when signals change.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example:&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="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="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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Count changed:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;count&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;
&lt;strong&gt;Inspect the dependency graph:&lt;/strong&gt; Angular provides debugging tools that show signal dependencies and which computations are dirty. Use these to spot unexpected missing links.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Signals and templates: How Angular wires it all up
&lt;/h2&gt;

&lt;p&gt;When you use signals in templates, Angular calls the signal to get its current value during change detection. This access causes Angular to register the component as a subscriber to the signal.&lt;/p&gt;

&lt;p&gt;So, if you write:&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;div&amp;gt;&lt;/span&gt;{{ count() }}&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Angular knows this component depends on &lt;code&gt;count&lt;/code&gt;. When &lt;code&gt;count&lt;/code&gt; updates, Angular marks this component dirty, triggering a re-render.&lt;/p&gt;

&lt;h2&gt;
  
  
  Computed signals: Laziness and caching
&lt;/h2&gt;

&lt;p&gt;Computed signals are lazy. They only re-run their function when accessed and when one of their dependencies has changed.&lt;/p&gt;

&lt;p&gt;That means if you have a computed signal that isn’t read by anything, it won’t run. This can trip you up if you expect side effects inside computed signals ,  those should go inside &lt;code&gt;effect()&lt;/code&gt; instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  One more thing: Batch updates
&lt;/h2&gt;

&lt;p&gt;Angular batches synchronous signal updates to avoid redundant change detection runs. If you update multiple signals synchronously, Angular runs change detection once after all updates complete.&lt;/p&gt;

&lt;p&gt;This is why sometimes your UI updates only after all state changes finish, which is a good thing for performance but can confuse debugging if you expect immediate UI changes after each setter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Angular Signals bring a fresh reactive model to Angular apps, but their magic depends on how Angular tracks dependencies and integrates with change detection. If you find your UI not updating, check if your signals are updated inside Angular’s zone and if components properly subscribe to those signals.&lt;/p&gt;

&lt;p&gt;Remember:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Accessing signals during component rendering registers dependencies.&lt;/li&gt;
&lt;li&gt;Signal updates mark components dirty to trigger change detection.&lt;/li&gt;
&lt;li&gt;Updates outside Angular’s zone need manual zone re-entry.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;effect()&lt;/code&gt; for side effects, not computed signals.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Getting these right will save you from mysterious stale UI bugs and make your Angular apps feel snappy and reliable.&lt;/p&gt;

&lt;p&gt;If you’re diving into Angular Signals, keep an eye on these mechanisms under the hood ,  they’re the secret sauce behind smooth reactivity.&lt;/p&gt;




&lt;h2&gt;
  
  
  Helpful learning resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.w3.org/WAI/" rel="noopener noreferrer"&gt;W3C WAI accessibility guidance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/company/w3schools-com" rel="noopener noreferrer"&gt;W3Schools accessibility tutorials&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.mozilla.org/" rel="noopener noreferrer"&gt;MDN Web Docs&lt;/a&gt;
Originally published at &lt;a href="https://blog.mspk.me/posts/how-angular-signals-work-under-the-hood-reactive-primitives-and-change-detection" rel="noopener noreferrer"&gt;Under The Hood&lt;/a&gt;.
Get the next deep dive in your inbox: &lt;a href="https://blog.mspk.me/#subscribe" rel="noopener noreferrer"&gt;subscribe to Under The Hood&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>angular</category>
      <category>reactivity</category>
      <category>signals</category>
      <category>changedetection</category>
    </item>
    <item>
      <title>Design Systems and Accessibility: Mechanisms to Enforce Consistent Focus Management</title>
      <dc:creator>Srikar Phani Kumar Marti</dc:creator>
      <pubDate>Mon, 05 Oct 2026 11:01:19 +0000</pubDate>
      <link>https://dev.to/mspk97/design-systems-and-accessibility-mechanisms-to-enforce-consistent-focus-management-3930</link>
      <guid>https://dev.to/mspk97/design-systems-and-accessibility-mechanisms-to-enforce-consistent-focus-management-3930</guid>
      <description>&lt;p&gt;Ever been puzzled when tabbing through a complex UI built from a design system, only to have the focus jump in unexpected ways or disappear entirely? Maybe a modal’s close button isn’t reachable by keyboard, or focus lands on something visually hidden. It’s a tiny annoyance for some, but a showstopper for keyboard users and screen reader folks.&lt;/p&gt;

&lt;p&gt;I’ve faced this exact headache while working on a large React design system. The components promised accessibility out of the box, but subtle app-level overrides or custom wrappers sometimes broke the focus flow. Tracking down where the focus management logic lived, and why it wasn’t working, led me down a rabbit hole of internal mechanisms that design systems use to enforce consistent, accessible focus.&lt;/p&gt;

&lt;p&gt;Let me share what I learned about how design systems coordinate focus, how they integrate with ARIA, and how you can debug when your app’s overrides clash with those defaults.&lt;/p&gt;

&lt;h2&gt;
  
  
  Focus management is more than tabindex
&lt;/h2&gt;

&lt;p&gt;Setting &lt;code&gt;tabindex&lt;/code&gt; is the classic way to control keyboard focus order. But design systems go way beyond sprinkling tabindex attributes.&lt;/p&gt;

&lt;p&gt;For example, take a dropdown component. When you open it, focus should move into the dropdown’s first interactive item. When you close it, focus needs to return to the button that triggered it. And keyboard navigation inside the dropdown needs to cycle logically.&lt;/p&gt;

&lt;p&gt;Design systems bake these rules into their components using JavaScript focus management. They listen for keyboard events, programmatically call &lt;code&gt;.focus()&lt;/code&gt; on appropriate elements, and maintain internal state about what should be focused next.&lt;/p&gt;

&lt;p&gt;This means a lot of logic lives inside the system’s components, not just in markup.&lt;/p&gt;

&lt;h2&gt;
  
  
  How design systems enforce consistent focus
&lt;/h2&gt;

&lt;h2&gt;
  
  
  1. Centralized focus utilities
&lt;/h2&gt;

&lt;p&gt;Many systems include shared focus utilities: helper functions or hooks that set focus, trap focus within dialogs, or restore focus after navigation. These utilities handle quirks across browsers and assistive tech. &lt;/p&gt;

&lt;p&gt;For example, a "focus trap" utility listens for tab key presses and ensures focus cycles only within a modal’s interactive elements, preventing keyboard users from tabbing to elements behind the modal.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Focus context and state
&lt;/h2&gt;

&lt;p&gt;Some systems create a focus context that tracks the current focus target and allows nested components to coordinate. For example, a tab panel component and its tabs share focus state so that activating a tab moves focus correctly and updates the panel.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. ARIA integration
&lt;/h2&gt;

&lt;p&gt;ARIA attributes are part of the story but not the whole story. Design systems use ARIA roles like &lt;code&gt;role="dialog"&lt;/code&gt;, &lt;code&gt;aria-modal="true"&lt;/code&gt;, and &lt;code&gt;aria-labelledby&lt;/code&gt; to help screen readers understand component purpose and relationships.&lt;/p&gt;

&lt;p&gt;But focus management is still done with JavaScript. For instance, an accessible modal will set &lt;code&gt;aria-hidden="true"&lt;/code&gt; on background content and shift focus to the modal container when opened.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Keyboard event handling
&lt;/h2&gt;

&lt;p&gt;Design systems intercept keyboard events like Tab, Shift+Tab, Escape, and arrow keys to customize focus movement and component behavior.&lt;/p&gt;

&lt;p&gt;For example, arrow keys might move focus between menu items, Escape closes a modal and returns focus, and Tab cycles focus inside a focus trap.&lt;/p&gt;

&lt;h2&gt;
  
  
  When your app overrides break focus management
&lt;/h2&gt;

&lt;p&gt;I ran into bugs when app-level styles or wrappers overrode the design system’s components. Here are common pitfalls:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Custom wrappers missing focus forwarding:&lt;/strong&gt; If you wrap a focusable component but don’t forward refs properly, programmatic &lt;code&gt;.focus()&lt;/code&gt; calls inside the system fail silently.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CSS hiding focus indicators:&lt;/strong&gt; Overriding focus styles with &lt;code&gt;outline: none&lt;/code&gt; or other rules can make focus invisible.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Conflicting event handlers:&lt;/strong&gt; Adding keyboard handlers that prevent default behavior or stop propagation can break the system’s internal focus logic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dynamic rendering mismatches:&lt;/strong&gt; Rendering components conditionally without coordinating focus restoration leads to lost focus or unexpected jumps.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Debugging focus issues in design systems
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Use browser devtools’ accessibility inspectors
&lt;/h2&gt;

&lt;p&gt;Browsers like Chrome and Firefox have accessibility panes showing the accessibility tree and focusable elements. Use these to verify which elements are focusable and whether ARIA attributes are set correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Manual focus inspection
&lt;/h2&gt;

&lt;p&gt;Try tabbing through your app step by step. Note where focus lands, where it disappears, or where it loops unexpectedly.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;document.activeElement&lt;/code&gt; in the console to see the current focused element at any point.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check event listeners
&lt;/h2&gt;

&lt;p&gt;Inspect the event listeners on elements to see if keyboard events are handled or blocked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify ref forwarding
&lt;/h2&gt;

&lt;p&gt;If your components use React, ensure ref forwarding is correctly set up so the design system can call &lt;code&gt;.focus()&lt;/code&gt; on the right DOM node.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test with screen readers
&lt;/h2&gt;

&lt;p&gt;Keyboard focus and visual focus indicators are only part of accessibility. Test with screen readers like NVDA or VoiceOver to make sure focus changes align with announcements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example: Focus trap in a modal
&lt;/h2&gt;

&lt;p&gt;Here’s a simplified example of how a focus trap works inside a modal component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Modal&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;isOpen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onClose&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;children&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;modalRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isOpen&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;focusableElements&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;modalRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a[href], button, input, textarea, select, [tabindex]:not([tabindex="-1"])&lt;/span&gt;&lt;span class="dl"&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;firstElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;focusableElements&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&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;lastElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;focusableElements&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;focusableElements&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleKeyDown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&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;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Tab&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shiftKey&lt;/span&gt;&lt;span class="p"&gt;)&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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activeElement&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;firstElement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nx"&gt;lastElement&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="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;if &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="nx"&gt;activeElement&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;lastElement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nx"&gt;firstElement&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="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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Escape&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="nf"&gt;onClose&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="nx"&gt;modalRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;keydown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleKeyDown&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;firstElement&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;return &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="nx"&gt;modalRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;keydown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleKeyDown&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="nx"&gt;isOpen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onClose&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isOpen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"dialog"&lt;/span&gt; &lt;span class="na"&gt;aria-modal&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;modalRef&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;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 snippet traps focus inside the modal while it’s open and closes on Escape. Design systems build on this idea but handle edge cases and browser quirks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Focus management inside design systems is a mix of ARIA, JavaScript, event handling, and CSS working together. It’s not enough to add &lt;code&gt;tabindex&lt;/code&gt; and call it done.&lt;/p&gt;

&lt;p&gt;If you build or maintain a design system, consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Providing standardized focus utilities&lt;/li&gt;
&lt;li&gt;Documenting expected focus behavior for each component&lt;/li&gt;
&lt;li&gt;Testing with keyboard and screen readers&lt;/li&gt;
&lt;li&gt;Encouraging app teams to respect focus management and avoid harmful overrides&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re integrating a design system and hit focus bugs, dig into the event handling, ref forwarding, and CSS focus styles. Use browser devtools and assistive tech to observe what’s happening.&lt;/p&gt;

&lt;p&gt;These mechanisms might feel like plumbing, but getting focus right makes your UI genuinely usable for everyone.&lt;/p&gt;




&lt;h2&gt;
  
  
  Helpful learning resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.w3.org/WAI/" rel="noopener noreferrer"&gt;W3C WAI accessibility guidance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/company/w3schools-com" rel="noopener noreferrer"&gt;W3Schools accessibility tutorials&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.mozilla.org/" rel="noopener noreferrer"&gt;MDN Web Docs&lt;/a&gt;
Originally published at &lt;a href="https://blog.mspk.me/posts/design-systems-and-accessibility-mechanisms-to-enforce-consistent-focus-management" rel="noopener noreferrer"&gt;Under The Hood&lt;/a&gt;.
Get the next deep dive in your inbox: &lt;a href="https://blog.mspk.me/#subscribe" rel="noopener noreferrer"&gt;subscribe to Under The Hood&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>a11y</category>
      <category>designsystems</category>
      <category>frontend</category>
      <category>focusmanagement</category>
    </item>
  </channel>
</rss>
