<?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: Barsukov Nikita</title>
    <description>The latest articles on DEV Community by Barsukov Nikita (@nsbarsukov).</description>
    <link>https://dev.to/nsbarsukov</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F857015%2F62fc9803-5d13-4219-8601-661ca0ee3241.jpg</url>
      <title>DEV Community: Barsukov Nikita</title>
      <link>https://dev.to/nsbarsukov</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nsbarsukov"/>
    <language>en</language>
    <item>
      <title>What's New in Taiga UI v5: A Modern Angular UI Kit</title>
      <dc:creator>Barsukov Nikita</dc:creator>
      <pubDate>Fri, 03 Apr 2026 07:30:49 +0000</pubDate>
      <link>https://dev.to/angular/whats-new-in-taiga-ui-v5-a-modern-angular-ui-kit-7lm</link>
      <guid>https://dev.to/angular/whats-new-in-taiga-ui-v5-a-modern-angular-ui-kit-7lm</guid>
      <description>&lt;p&gt;&lt;a href="https://taiga-ui.dev" rel="noopener noreferrer"&gt;Taiga UI&lt;/a&gt; is an Open Source Angular UI library that helps developers build modern, reliable user interfaces. We recently shipped the &lt;strong&gt;fifth major release&lt;/strong&gt;, bringing a ton of architectural improvements and new capabilities. In this article, we'll take a deep dive into the highlights of version &lt;code&gt;5.0.0&lt;/code&gt; and explain why you should plan your upgrade as soon as possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fresh Minimum: Angular 19+
&lt;/h2&gt;

&lt;p&gt;In this new major version of our libraries, we raised the minimum supported Angular version to 19 and above. This means we can take advantage of all the new features and improvements added since Angular 16 (the minimum supported version in the previous Taiga UI major). And the paradigm shift toward a signal-based style has changed a lot in our codebase.&lt;/p&gt;

&lt;h3&gt;
  
  
  Signal Inputs
&lt;/h3&gt;

&lt;p&gt;All our hundreds of components and directives said goodbye to classic properties decorated with &lt;code&gt;@Input()&lt;/code&gt; and switched to their signal-based counterpart — &lt;a href="https://angular.dev/guide/components/inputs" rel="noopener noreferrer"&gt;input&lt;/a&gt;. Previously, our code had a lot of getters that internally used a private class method decorated with &lt;a href="https://taiga-ui.dev/v4/utils/pure" rel="noopener noreferrer"&gt;@tuiPure&lt;/a&gt; for memoization. Here's a simplified example of that approach:&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;Input&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;required&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="nx"&gt;fileName&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="c1"&gt;// Used in the template&lt;/span&gt;
&lt;span class="c1"&gt;// (potentially recalculated on every re-render)&lt;/span&gt;
&lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;return&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;getType&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;file&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="nd"&gt;tuiPure&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;getType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&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="kr"&gt;string&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;dot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lastIndexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;'&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;dot&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;)&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, with signal-based &lt;a href="https://angular.dev/guide/signals#computed-signals" rel="noopener noreferrer"&gt;computed&lt;/a&gt;, getters are no longer needed, and we can write cleaner, more readable code without extra optimizations. &lt;code&gt;computed&lt;/code&gt; takes care of memoization automatically.&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;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;required&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;type&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="p"&gt;{&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lastIndexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;'&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;dot&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;)&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're sending the &lt;code&gt;@tuiPure&lt;/code&gt; decorator off into honorable retirement: you were a good friend and helper for many years — thank you!&lt;/p&gt;

&lt;h3&gt;
  
  
  Signal viewChild(-ren) / contentChild(-ren)
&lt;/h3&gt;

&lt;p&gt;In Taiga components, we frequently used the &lt;a href="https://v16.angular.io/api/core/ViewChild" rel="noopener noreferrer"&gt;@ViewChild(‑ren)&lt;/a&gt; and &lt;a href="https://v16.angular.io/api/core/ContentChild" rel="noopener noreferrer"&gt;@ContentChild(‑ren)&lt;/a&gt; decorators. You decorate a property in a component — and the requested DOM element gets automatically assigned to it. A significant limitation of this approach was that these elements only became available in the &lt;code&gt;AfterViewInit&lt;/code&gt; and &lt;code&gt;AfterContentInit&lt;/code&gt; lifecycle hooks. As a result, constructs like this could pop up in our codebase:&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AnyComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;contentReady$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ReplaySubject&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

   &lt;span class="nx"&gt;children$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contentReady$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="nf"&gt;switchMap&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;childrenQuery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;changes&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="nd"&gt;ContentChildren&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ref&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="nx"&gt;childrenQuery&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;QueryList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;any&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;ngAfterContentInit&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;contentReady$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&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="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 &lt;code&gt;contentReady$&lt;/code&gt; stream notifies all its observers that it's safe to proceed. The trick of creating such a stream made the code slightly more declarative compared to directly manipulating all observers inside a hook. But it still produced its fair share of boilerplate.&lt;/p&gt;

&lt;p&gt;Signal-based &lt;code&gt;viewChild(-ren)&lt;/code&gt; and &lt;code&gt;contentChild(-ren)&lt;/code&gt; are a real game-changer. You can practically forget about the &lt;code&gt;AfterViewInit&lt;/code&gt; and &lt;code&gt;AfterContentInit&lt;/code&gt; hooks, because signals created via &lt;code&gt;viewChild&lt;/code&gt; and &lt;code&gt;contentChild&lt;/code&gt; know when to update on their own, and all their subscribers automatically recalculate reactively. All those lines from the old approach are replaced by a single built-in one-liner.&lt;/p&gt;

&lt;h3&gt;
  
  
  Signals Over RxJS
&lt;/h3&gt;

&lt;p&gt;Even though our team deeply respects RxJS and is confident it'll be around for a long time, we genuinely fell in love with signals. We decided to make them the priority in our public API — not just because of their growing popularity in the Angular community, but because for many reactive tasks they're a much better fit than RxJS, letting us provide better support and write more optimal, understandable code.&lt;/p&gt;

&lt;p&gt;Here are just a few examples of changes from the new major version — the updated &lt;a href="https://github.com/taiga-family/taiga-ui/pull/12397" rel="noopener noreferrer"&gt;TUI_NUMBER_FORMAT&lt;/a&gt; and &lt;a href="https://github.com/taiga-family/taiga-ui/pull/12373" rel="noopener noreferrer"&gt;TUI_DATE_FORMAT&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before&lt;/span&gt;
&lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;TUI_NUMBER_FORMAT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Observable&amp;lt;TuiNumberFormatSettings&amp;gt;&lt;/span&gt;
&lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;TUI_DATE_FORMAT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Observable&amp;lt;TuiDateFormatSettings&amp;gt;&lt;/span&gt;

&lt;span class="c1"&gt;// After &lt;/span&gt;
&lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;TUI_NUMBER_FORMAT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Signal&amp;lt;TuiNumberFormatSettings&amp;gt;&lt;/span&gt;
&lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;TUI_DATE_FORMAT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Signal&amp;lt;TuiDateFormatSettings&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To sum up the thought about the dawn of the "signal era" in the Taiga codebase: we don't resist the new trends set by the Angular team. And most importantly, we don't prevent our users from seamlessly harnessing the full power of signals when building applications with Taiga UI!&lt;/p&gt;

&lt;p&gt;We've already seen the benefits of the signal-based style firsthand. Less boilerplate, better performance, dramatically fewer Change Detection bugs, and improved support for Zoneless applications!&lt;/p&gt;

&lt;h3&gt;
  
  
  New Esbuild + Vite Build Engine
&lt;/h3&gt;

&lt;p&gt;Another important change driven by raising the minimum Angular version is the switch to the &lt;a href="https://angular.dev/tools/cli/build-system-migration" rel="noopener noreferrer"&gt;modern Esbuild + Vite build engine&lt;/a&gt;. This move not only improved our own Developer Experience (build time during local development was significantly reduced), but also allowed us to catch bugs in YOUR Esbuild applications (the default for all new Angular apps) before we even release our libraries.&lt;/p&gt;

&lt;p&gt;And these aren't just words — we've already fixed several esbuild incompatibility bugs related to circular dependencies (&lt;a href="https://github.com/taiga-family/taiga-ui/pull/12435" rel="noopener noreferrer"&gt;#12435&lt;/a&gt; and &lt;a href="https://github.com/taiga-family/taiga-ui/pull/12438" rel="noopener noreferrer"&gt;#12438&lt;/a&gt;), as well as CSS selector specificity issues (&lt;a href="https://github.com/taiga-family/taiga-ui/pull/12544" rel="noopener noreferrer"&gt;#12544&lt;/a&gt;, &lt;a href="https://github.com/taiga-family/taiga-ui/pull/12543" rel="noopener noreferrer"&gt;#12543&lt;/a&gt;, &lt;a href="https://github.com/taiga-family/taiga-ui/pull/12553" rel="noopener noreferrer"&gt;#12553&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Control Flow
&lt;/h3&gt;

&lt;p&gt;Before Control Flow appeared, the Taiga UI team had already been publishing the structural directive &lt;a href="https://taiga-ui.dev/v4/directives/let" rel="noopener noreferrer"&gt;*tuiLet&lt;/a&gt; for many years, which allowed you to declare a local variable in a template. And as our company's codebase shows, this directive was used over 6,000 times. The local variable declaration approach was extremely popular. It has now been replaced by the new built-in &lt;code&gt;@let&lt;/code&gt; syntax.&lt;/p&gt;

&lt;p&gt;The Taiga &lt;code&gt;*tuiLet&lt;/code&gt; directive is now gracefully retiring, making way for the built-in &lt;code&gt;@let&lt;/code&gt; syntax — which, as a nice bonus, requires zero imports!&lt;/p&gt;

&lt;h2&gt;
  
  
  A Shiny New Component Showcase
&lt;/h2&gt;

&lt;p&gt;We've significantly reworked the design and content of our documentation. It now features improved navigation and a more intuitive interface.&lt;/p&gt;

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

&lt;p&gt;I'd also like to remind you that the engine behind our documentation is a reusable solution published as the npm package &lt;a href="https://github.com/taiga-family/taiga-ui/blob/main/projects/addon-doc/README.md" rel="noopener noreferrer"&gt;@taiga-ui/addon-doc&lt;/a&gt;. It's already actively used not only for Taiga UI docs, but also in other Taiga Family products: &lt;a href="https://maskito.dev" rel="noopener noreferrer"&gt;Maskito&lt;/a&gt;, &lt;a href="https://taiga-family.github.io/editor" rel="noopener noreferrer"&gt;Taiga Editor&lt;/a&gt;, &lt;a href="https://taiga-family.github.io/ng-morph" rel="noopener noreferrer"&gt;Ng Morph&lt;/a&gt;, and &lt;a href="https://taiga-family.github.io/ng-draw-flow" rel="noopener noreferrer"&gt;Ng Draw Flow&lt;/a&gt;. This package keeps evolving and gaining new features (for example, a brand-new Table of Contents component was added in this release). You can always use &lt;code&gt;@taiga-ui/addon-doc&lt;/code&gt; to build documentation for your own library!&lt;/p&gt;

&lt;h2&gt;
  
  
  Browser Bump
&lt;/h2&gt;

&lt;p&gt;Here's a little secret: the favorite part of every major release for Taiga UI maintainers is bumping the minimum supported browser versions. It unlocks new capabilities and features that we happily use in our libraries to write even cleaner and more reliable code.&lt;/p&gt;

&lt;p&gt;Already in Taiga UI v4, we set a course for adding RTL support to our libraries. That's right — we expanded our audience to include Middle Eastern and Central Asian products!&lt;br&gt;
With the new browser support, we got access to an even wider list of logical CSS properties, such as &lt;a href="https://developer.mozilla.org/docs/Web/CSS/Reference/Properties/inset#browser_compatibility" rel="noopener noreferrer"&gt;inset&lt;/a&gt;, &lt;a href="https://developer.mozilla.org/docs/Web/CSS/Reference/Properties/padding-inline#browser_compatibility" rel="noopener noreferrer"&gt;padding-inline&lt;/a&gt;, &lt;a href="https://developer.mozilla.org/docs/Web/CSS/Reference/Properties/margin-inline#browser_compatibility" rel="noopener noreferrer"&gt;margin-inline&lt;/a&gt;, and many others.&lt;/p&gt;

&lt;p&gt;RTL support has become even easier to maintain, and the code is more robust (since it's now handled by native &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Logical_properties_and_values" rel="noopener noreferrer"&gt;logical CSS properties&lt;/a&gt; instead of our old fallbacks and workarounds).&lt;/p&gt;

&lt;p&gt;We also got access to &lt;a href="https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/BigInt" rel="noopener noreferrer"&gt;BigInt&lt;/a&gt;. It feels nice to finally have access to all JavaScript data types — at least by 2026! :D&lt;br&gt;
We immediately put this to use and added &lt;code&gt;BigInt&lt;/code&gt; support to our &lt;code&gt;@maskito/kit&lt;/code&gt; library in the &lt;a href="https://maskito.dev/kit/number" rel="noopener noreferrer"&gt;Number&lt;/a&gt; mask and in our Taiga &lt;a href="https://taiga-ui.dev/components/input-number#big-int-as-form-control-value" rel="noopener noreferrer"&gt;InputNumber&lt;/a&gt; component.&lt;/p&gt;
&lt;h2&gt;
  
  
  Angular Animations — Goodbye!
&lt;/h2&gt;

&lt;p&gt;Starting with Angular 20.2, the &lt;code&gt;@angular/animations&lt;/code&gt; package has been marked as &lt;code&gt;deprecated&lt;/code&gt;. And the official Angular documentation now &lt;a href="https://angular.dev/guide/animations/migration" rel="noopener noreferrer"&gt;reads as follows&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can replace all animations based on &lt;code&gt;@angular/animations&lt;/code&gt; with plain CSS or JS animation libraries. Removing &lt;code&gt;@angular/animations&lt;/code&gt; from your application can significantly reduce the size of your JavaScript bundle. Native CSS animations generally offer superior performance, as they can benefit from hardware acceleration. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And we responded promptly. In Taiga UI v5, we completely dropped the &lt;code&gt;@angular/animations&lt;/code&gt; package: all existing animations have been rewritten using pure native CSS, and &lt;code&gt;@angular/animations&lt;/code&gt; has been entirely removed from the dependency list of our packages! No more mandatory &lt;code&gt;provideAnimations()&lt;/code&gt; just to get Taiga UI up and running.&lt;/p&gt;

&lt;p&gt;The trickiest part was natively rewriting the logic around the former &lt;a href="https://v19.angular.dev/guide/animations/transition-and-triggers#animate-entering-and-leaving-a-view" rel="noopener noreferrer"&gt;:leave&lt;/a&gt; mechanism. Currently, there's no convenient CSS alternative for animating an element leaving the DOM without timers and boilerplate code. But my colleague found an &lt;a href="https://www.angularspace.com/how-to-get-rid-of-angular-animations-right-now" rel="noopener noreferrer"&gt;elegant solution&lt;/a&gt; to this problem as well. The &lt;code&gt;@taiga-ui/cdk&lt;/code&gt; library got a new &lt;a href="https://taiga-ui.dev/directives/animated" rel="noopener noreferrer"&gt;Animated&lt;/a&gt; directive. Just slap this directive on any DOM element — when it appears or disappears, the element gets a CSS class &lt;code&gt;tui-enter&lt;/code&gt; / &lt;code&gt;tui-leave&lt;/code&gt;, and you can define any CSS animations for that class. All without code boilerplate and in the true Angular way!&lt;/p&gt;

&lt;p&gt;It's also great that we're moving in the same direction as the Angular team. The &lt;code&gt;Animated&lt;/code&gt; directive neatly anticipated the future syntax that the Angular team introduced very recently in their &lt;a href="https://blog.angular.dev/announcing-angular-v21-57946c34f14b" rel="noopener noreferrer"&gt;v21 announcement&lt;/a&gt; — &lt;a href="https://angular.dev/guide/animations" rel="noopener noreferrer"&gt;animate.enter and animate.leave&lt;/a&gt;. Migrating from our solution to the built-in Angular syntax will be as simple as it gets — just replace the &lt;code&gt;tuiAnimated&lt;/code&gt; directive on an element with &lt;code&gt;animate.enter="tui-enter"&lt;/code&gt; and &lt;code&gt;animate.leave="tui-leave"&lt;/code&gt; (and keep the CSS files unchanged).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A nice bonus: our &lt;code&gt;Animated&lt;/code&gt; directive has been cherry-picked into the fourth major version of Taiga, so you can start gradually moving away from Angular animations in your app right now — even before you begin upgrading your Taiga UI version, and even if your app's Angular version is significantly older than 20.2. And when you upgrade Taiga to v5, &lt;code&gt;@angular/animations&lt;/code&gt; will simply disappear from your &lt;code&gt;node_modules&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Simplified Taiga UI Setup for Your Project
&lt;/h2&gt;

&lt;p&gt;Inspired by the modern Angular naming conventions for DI utility functions (&lt;a href="https://angular.dev/api/router/provideRouter" rel="noopener noreferrer"&gt;provideRouter&lt;/a&gt;, &lt;a href="https://angular.dev/api/ssr/provideServerRendering" rel="noopener noreferrer"&gt;provideServerRendering&lt;/a&gt;, &lt;a href="https://angular.dev/api/core/provideZoneChangeDetection" rel="noopener noreferrer"&gt;provideZoneChangeDetection&lt;/a&gt;, etc.), we decided to simplify the process of adding Taiga UI to your project. Now, to set up the DI tree, all you need is a single call to &lt;a href="https://taiga-ui.dev/getting-started/Manual#provide-config" rel="noopener noreferrer"&gt;provideTaiga()&lt;/a&gt;, which automatically provides all the necessary dependencies and initial configurations for our components to work.&lt;/p&gt;

&lt;p&gt;For you, it's just one function call, but under the hood it takes care of &lt;a href="https://github.com/taiga-family/utils/tree/main/projects/font-watcher" rel="noopener noreferrer"&gt;automatic font scaling&lt;/a&gt;, dark/light theme setup, and other internal configurations needed for Taiga UI components to function correctly!&lt;/p&gt;
&lt;h2&gt;
  
  
  New Text Fields Are Now a Stable API
&lt;/h2&gt;

&lt;p&gt;We kicked off the large-scale text field refactoring back in the previous major version.&lt;br&gt;
At that time, we moved all our old control versions into the &lt;code&gt;@taiga-ui/legacy&lt;/code&gt; package (to ensure a smooth migration path for users to the new public API) and started rewriting each component from scratch using fresh design specs and modern Angular features (decomposition via host directives + signals).&lt;/p&gt;

&lt;p&gt;The task turned out to be quite challenging and extremely labor-intensive, but the results will genuinely make you happy. The new text fields offer a declarative way to customize through HTML markup and a uniform public API across all types of controls. Once you understand the customization concepts for one type of text field, you can easily customize any other control — without even looking at the docs!&lt;/p&gt;

&lt;p&gt;Another key feature of the new text fields is their openness. Everything you need is exposed, so you can bring even the wildest ideas to life. Just take a look at this &lt;code&gt;InputDate&lt;/code&gt; example:&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;tui-textfield&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;tuiLabel&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Choose a date&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;tuiInputDate&lt;/span&gt; &lt;span class="na"&gt;[formControl]=&lt;/span&gt;&lt;span class="s"&gt;"control"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;ng-container&lt;/span&gt; &lt;span class="na"&gt;*tuiDropdown&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;tui-calendar&lt;/span&gt; &lt;span class="na"&gt;[markerHandler]=&lt;/span&gt;&lt;span class="s"&gt;"markerHandler"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt;
      &lt;span class="na"&gt;tuiButton&lt;/span&gt;
      &lt;span class="na"&gt;(click)=&lt;/span&gt;&lt;span class="s"&gt;"..."&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      Today
    &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/ng-container&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/tui-textfield&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;First, all the input parameters of the &lt;code&gt;&amp;lt;tui-calendar /&amp;gt;&lt;/code&gt; component are fully at our disposal — no prop drilling like in the previous version of this control! Second, we can freely add any elements (even interactive ones!) to the calendar dropdown — for example, a "Today" button with a custom click handler!&lt;/p&gt;

&lt;p&gt;And this applies to every type of text field (and there are over 15 of them!).&lt;br&gt;
With the release of the fifth major version, our large-scale text field refactoring is officially complete. Their public API is fully stabilized, and the previous generation of these controls has been permanently removed from the &lt;code&gt;@taiga-ui/legacy&lt;/code&gt; package.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For those who prefer gradual changes in life and code, we cherry-picked all important fixes for the new text fields into the fourth major version, making the migration to the new major as smooth as possible. Before upgrading to Taiga v5, you can iteratively update your codebase, mixing old and new text fields, so the upcoming migration is as comfortable and painless as it can be.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Improved Icon Component
&lt;/h2&gt;

&lt;p&gt;In the &lt;a href="https://medium.com/angularwave/introducing-taiga-ui-v4-df6c7c62330f#2434" rel="noopener noreferrer"&gt;previous major release announcement&lt;/a&gt;, I mentioned that for icons we moved away from the old approach of using &lt;a href="https://developer.mozilla.org/ru/docs/Web/SVG/Element/svg" rel="noopener noreferrer"&gt;&amp;lt;svg /&amp;gt;&lt;/a&gt; with the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/SVG/Element/use" rel="noopener noreferrer"&gt;&amp;lt;use /&amp;gt;&lt;/a&gt; tag in favor of CSS masks.&lt;/p&gt;

&lt;p&gt;Back then, the new approach was implemented in the &lt;a href="https://taiga-ui.dev/components/icon" rel="noopener noreferrer"&gt;Icon&lt;/a&gt; component and brought numerous benefits: the ability to store icons on a CDN and render them without DOM manipulations or a sanitizer, automatic browser caching, and a simplified way to scale icons.&lt;/p&gt;

&lt;p&gt;But we didn't stop there and continued to develop this further.&lt;br&gt;
Starting with the fifth major version, this component can do significantly more:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You can now control not only the icon size but also the stroke width of the icon's content. This capability is demonstrated really well in this interactive &lt;a href="https://taiga-ui.dev/components/icon#parameters" rel="noopener noreferrer"&gt;documentation example&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The updated component supports not only classic single-color icons but also multi-color and font-based icons! They're managed through the &lt;code&gt;@tui&lt;/code&gt;, &lt;code&gt;@img&lt;/code&gt;, and &lt;code&gt;@font&lt;/code&gt; prefixes, and the component figures out under the hood how to render the icon based on its type: via CSS mask, &lt;code&gt;background-image&lt;/code&gt;, or font + &lt;code&gt;content&lt;/code&gt; on the &lt;code&gt;:before&lt;/code&gt; pseudo-element. You can see it in action in &lt;a href="https://taiga-ui.dev/components/icon#basic" rel="noopener noreferrer"&gt;this example&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  And Much More!
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Portals have been completely rewritten and streamlined into a single grid-based container. It's now even easier to &lt;a href="https://taiga-ui.dev/components/dialog#customization" rel="noopener noreferrer"&gt;customize dialogs&lt;/a&gt;, simpler to manage &lt;a href="https://taiga-ui.dev/components/notification" rel="noopener noreferrer"&gt;notifications&lt;/a&gt; and their subtypes (&lt;a href="https://taiga-ui.dev/components/notification/API?block=end&amp;amp;inline=start" rel="noopener noreferrer"&gt;screen position&lt;/a&gt;, &lt;a href="https://taiga-ui.dev/components/toast#service" rel="noopener noreferrer"&gt;number of simultaneous instances&lt;/a&gt;), and easier to create your own &lt;a href="https://taiga-ui.dev/portals" rel="noopener noreferrer"&gt;custom portal entities&lt;/a&gt; through new abstract classes and services.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A new &lt;a href="https://taiga-ui.dev/components/popout" rel="noopener noreferrer"&gt;Popout&lt;/a&gt; service that simplifies displaying custom content in &lt;a href="https://developer.mozilla.org/docs/Web/API/Picture-in-Picture_API" rel="noopener noreferrer"&gt;Picture-in-Picture&lt;/a&gt; mode or opening a piece of your application in an entirely new tab.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We stabilized the use of the &lt;a href="https://developer.mozilla.org/docs/Web/API/CloseWatcher" rel="noopener noreferrer"&gt;CloseWatcher&lt;/a&gt; web API in Taiga dialogs. Now, when a dialog is open on an Android device and the user taps the browser's "Back" button, the dialog simply closes — instead of navigating to the previous route behind the open dialog.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A revamped &lt;a href="https://taiga-ui.dev/components/accordion" rel="noopener noreferrer"&gt;Accordion&lt;/a&gt;. We're continuing the trend (that accompanies every Taiga major version) of minimal DOM element nesting in our components, which should make it easier for you to customize them and set native attributes like ARIA attributes or id — and now the accordion has joined the ranks of the "lucky ones."&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/taiga-family/utils/tree/main/projects/font-watcher" rel="noopener noreferrer"&gt;Automatic font scaling&lt;/a&gt; is now enabled by default. Many users rely on iOS/Android system accessibility settings to increase the font size across all interfaces on their mobile devices — including web apps. Taiga components can read this system setting and adapt, making your interfaces comfortable for users with visual impairments.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We dropped the use of our custom decorators — you're free to build your application with any value of the TypeScript &lt;a href="https://www.typescriptlang.org/tsconfig/#experimentalDecorators" rel="noopener noreferrer"&gt;experimentalDecorators&lt;/a&gt; flag without worrying that Taiga UI components will stop working.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We're continuing to actively develop &lt;a href="https://taiga-ui.dev/ai-support" rel="noopener noreferrer"&gt;AI tooling&lt;/a&gt; support for working with Taiga UI components. Our &lt;a href="https://github.com/taiga-family/taiga-ui-mcp" rel="noopener noreferrer"&gt;MCP server&lt;/a&gt; now supports the new major version as well. We're also closely watching the development of &lt;a href="https://github.com/webmachinelearning/webmcp" rel="noopener noreferrer"&gt;WebMCP&lt;/a&gt; to integrate its support into our documentation when the time comes.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And a huge number of other improvements and new features that you can find in the &lt;a href="https://github.com/taiga-family/taiga-ui/releases/tag/v5.0.0" rel="noopener noreferrer"&gt;release notes&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Upgrade?
&lt;/h2&gt;

&lt;p&gt;We did our best to make the migration to the new version as smooth as possible. To that end, we've prepared a bunch of migration scripts that will automatically analyze your code and fix most breaking changes — all with a single console command! And anything that was too complex to fix automatically via AST manipulations has been annotated with comments right in the code — complete with hints and links so you can easily finish the migration on your own or with the help of AI agents.&lt;/p&gt;

&lt;p&gt;Detailed instructions for running the migration schematics and the necessary preparation steps are available in our &lt;a href="https://taiga-ui.dev/migration-guide" rel="noopener noreferrer"&gt;upgrade guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you run into any issues or bugs during the upgrade, don't hesitate to open an &lt;a href="https://github.com/taiga-family/taiga-ui/issues/new/choose" rel="noopener noreferrer"&gt;issue&lt;/a&gt; or ask a question in our &lt;a href="https://t.me/taiga_ui/8242" rel="noopener noreferrer"&gt;Telegram community&lt;/a&gt;. The Taiga team can't wait for your feedback!&lt;/p&gt;

&lt;h2&gt;
  
  
  See You Soon!
&lt;/h2&gt;

&lt;p&gt;To wrap up, let me just give you a little sneak peek at our future plans.&lt;/p&gt;

&lt;p&gt;Very soon you'll see revamped date picker components — our design team has already delivered fresh design specs with a more modern look for the calendars. During the rework, we plan to address the accumulated feature requests around their customization.&lt;/p&gt;

&lt;p&gt;We're also keeping a close eye on the development of &lt;a href="https://angular.dev/essentials/signal-forms" rel="noopener noreferrer"&gt;signal-based forms&lt;/a&gt;. As of this writing, the new API is still rough around the edges and continues to undergo changes. As soon as it fully stabilizes, we'll make sure Taiga controls support them.&lt;/p&gt;

&lt;p&gt;Finally, one of our big goals for this year is to keep pushing the accessibility of our components forward! All Taiga components traditionally support keyboard navigation, screen readers, and other assistive technologies, but accessibility is a broader topic and we plan to dedicate even more attention to it this year.&lt;/p&gt;

&lt;p&gt;See you in upcoming articles and releases!&lt;/p&gt;

&lt;p&gt;And a reminder: the easiest way to say thank you is to give us a star on &lt;a href="https://github.com/taiga-family/taiga-ui" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>javascript</category>
      <category>frontend</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Taiga UI: Year of the Tiger</title>
      <dc:creator>Barsukov Nikita</dc:creator>
      <pubDate>Thu, 22 Dec 2022 10:00:37 +0000</pubDate>
      <link>https://dev.to/angular/taiga-ui-year-of-the-tiger-9ih</link>
      <guid>https://dev.to/angular/taiga-ui-year-of-the-tiger-9ih</guid>
      <description>&lt;p&gt;The year 2022 was extremely fruitful for our Taiga UI team and, what is more important, we picked up tiger speed in terms of updates and improvements of the library. Within this article, I would like to develop a habit of annual reports about the progress, which might be handy for us to recollect all achievements and failures in order to adjust our strategy for the upcoming years.&lt;/p&gt;

&lt;p&gt;If you aren’t acquainted with Taiga and randomly stumbled upon this text, let me introduce &lt;a href="https://taiga-ui.dev" rel="noopener noreferrer"&gt;Taiga UI&lt;/a&gt; — a powerful set of components for Angular, which is used in dozens of Tinkoff’s products and projects! In the following sections I will briefly unpack the overall achievements related to this multifaceted library.&lt;/p&gt;




&lt;h2&gt;
  
  
  One more year in Open Source
&lt;/h2&gt;

&lt;p&gt;The development of the library has been going on for several years, two of which are in Open Source. We are happy to share our product with the community, and the source code is publically available. Despite the naming, we do not want to be the only people in our forest, hence we eagerly invite everyone else to join the development of the UI Kit!  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/waterplea"&gt;Alex Inkin&lt;/a&gt; has already summed up the project’s results for the first year in Open Source. He described how we organized all processes so that the community could easily contribute to our project. Read more details about our last year's achievements in the article:&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/angular" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F802%2F39aa2792-6183-496a-8513-1a38c76dbfb6.png" alt="Angular" width="800" height="800"&gt;
      &lt;div class="ltag__link__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F442490%2F440f898d-bfb2-4d96-9492-45ca5ccc6416.jpg" alt="" width="460" height="460"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/angular/taiga-ui-a-year-in-open-source-416l" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Taiga UI: A year in Open Source&lt;/h2&gt;
      &lt;h3&gt;Alex Inkin for Angular ・ Dec 24 '21&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#angular&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#opensource&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#typescript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;The Taiga UI library has a core team that hones and polishes the project's codebase. However, an impressive amount of the improvements come from external contributors. By the end of 2022, their number &lt;a href="https://github.com/Tinkoff/taiga-ui/graphs/contributors" rel="noopener noreferrer"&gt;exceeded a hundred people&lt;/a&gt;! Our library is used by a variety of companies from various countries: most oftenly documentation is accessed from Russia, Vietnam, Belarus, USA, India and France. The geography of our users is pretty impressive! &lt;/p&gt;

&lt;p&gt;In 2022, external contributors opened over two hundred Pull Requests, which is ~ 15% of all opened PRs for the entire year. Thank you very much to all our contributors for such an active participation in the project! &lt;/p&gt;

&lt;p&gt;In October, it was a time for an international online festival devoted to Open Source — &lt;a href="https://hacktoberfest.com" rel="noopener noreferrer"&gt;Hacktoberfest&lt;/a&gt;. Obviously, to stay in the loop and showcase our offspring, the team prepared Taiga UI for participation in this event.&lt;/p&gt;

&lt;h2&gt;
  
  
  One more major release
&lt;/h2&gt;

&lt;p&gt;In the last days of summer, we introduced the third major release of the library.&lt;/p&gt;

&lt;p&gt;We tried to postpone the introduction of breaking changes to our components as much as possible in order to cause little hassle to Taiga UI users. When there was a need to refactor the code and somehow change the public API of the components, we always keeped backward compatibility with the old interfaces.&lt;/p&gt;

&lt;p&gt;Long-term efforts to maintain backward compatibility of the product have their costs — there is more and more legacy code, which increases the bundle of product applications. Another reason why breaking changes cannot be avoided is that it is getting harder to maintain the code with every new day. Sooner or later the moment of great changes comes.&lt;/p&gt;

&lt;p&gt;More than a year and a half has passed since the last major release 2.0, and we decided that the moment had come. We were preparing the new release for the whole summer. The list of changes within the new version is enormous, and not to kill the vibe of the article, I will highlight the most curious ones:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Angular update&lt;/strong&gt;. The Angular framework has a &lt;a href="https://angular.io/guide/creating-libraries#ensuring-library-version-compatibility" rel="noopener noreferrer"&gt;rule&lt;/a&gt;: if a library is published under any of the older versions of Angular, then it can be used in applications with more modern versions of the framework without problems. But not vice versa! If the library was published under a modern version of Angular (for example, 14.x.x), then it will no longer be possible to use it in applications with an older version of the framework (in our example, this is 13.x.x and less).&lt;/p&gt;

&lt;p&gt;In Tinkoff, a plethora of projects use Taiga UI. Some of them use only the most modern versions of all technologies. However, there are some projects with a lower priority which can’t be updated so frequently and don’t use the most up-to-date versions of Angular.&lt;/p&gt;

&lt;p&gt;Therefore, we were postponing the increasing the threshold of the minimum supported version of Angular and all this time Taiga UI used the 9th-version and published with the ViewEngine legacy engine. But in 3.0, we bumped Angular to the 12th version and also began publishing using the modern Ivy engine! Now users of our library can finally experience all the benefits of a modern engine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dropped support for ancient browsers&lt;/strong&gt;. Every front-end developer knows what a great feeling that is! We already weren’t supporting Internet Explorer for a long time, but now it's time for those versions of Edge that are based on the ancient EdgeHTML engine. Maintenance for these browsers was extremely cumbersome, so it was more efficient to cut them off, than to keep them alive, and moreover, the demand for these browsers wasn’t there — the number of users is measured in tenths of a percent.&lt;/p&gt;

&lt;p&gt;In addition, we bumped the minimum threshold for support for the problematic Safari browser — now it's 12.1+. If the benefit of these solutions is obvious to Taiga UI developers, is it good for our library users?&lt;/p&gt;

&lt;p&gt;Definitely yes. Now during development we can use modern browser capabilities, introduce less extra code as polyfills, and drop many different workarounds from the code that existed only for outdated browsers. Our code occupies less storage and is much more stable!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Global styles have become optional&lt;/strong&gt;. In the third version of Taiga UI, all global styles were moved into a separate package and became optional in usage. Taiga UI has become even more flexible!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Huge component refactoring&lt;/strong&gt;. The new major release provides us with an opportunity to discard a large number of legacy components’ API and introduce a new one.&lt;/p&gt;

&lt;p&gt;In 3.0, many old components got a fresh appearance — after refactoring they became more convenient to use and easier to maintain. The most remarkable examples are the &lt;a href="https://taiga-ui.dev/editor/getting-started" rel="noopener noreferrer"&gt;Editor&lt;/a&gt;, &lt;a href="https://taiga-ui.dev/components/input-files" rel="noopener noreferrer"&gt;InputFiles&lt;/a&gt;, all components with &lt;a href="https://taiga-ui.dev/directives/dropdown" rel="noopener noreferrer"&gt;dropdowns&lt;/a&gt; and &lt;a href="https://taiga-ui.dev/directives/hint" rel="noopener noreferrer"&gt;hints&lt;/a&gt;, and all components with &lt;a href="https://taiga-ui.dev/components/slider" rel="noopener noreferrer"&gt;sliders&lt;/a&gt;. You can read more about the latter in the article:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="/angular" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F802%2F39aa2792-6183-496a-8513-1a38c76dbfb6.png" alt="Angular" width="800" height="800"&gt;
      &lt;div class="ltag__link__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F857015%2F62fc9803-5d13-4219-8601-661ca0ee3241.jpg" alt="" width="800" height="1066"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/angular/sliders-are-like-onions-three-gradient-layers-of-a-single-slider-1nia" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Sliders are like onions: three gradient layers of a single slider&lt;/h2&gt;
      &lt;h3&gt;Barsukov Nikita for Angular ・ Jul 5 '22&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#angular&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#typescript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Migration schematics&lt;/strong&gt;. The process of updating major versions of any dependencies is always a big headache for product teams. Firstly, developers need to figure out what needs to be changed in the code. Moreover, such updates require a complete regression of applications.&lt;/p&gt;

&lt;p&gt;We desire that all teams migrate to up-to-date versions as soon as possible, so we paid great attention to the migration schematics to eliminate at least some of their concerns. Developer who has the second version of Taiga UI in their application just need to write &lt;code&gt;ng update @taiga-ui/cdk&lt;/code&gt; in the console to update all Taiga UI packages to the third version. It will solve most of the breaking changes. Such schematic scripts automatically go through all HTML, TypeScript and style project files, find outdated API in it and replace it with a new alternative.&lt;/p&gt;

&lt;p&gt;If you want to know more details about this major release, visit the page with its &lt;a href="https://github.com/Tinkoff/taiga-ui/releases/tag/v3.0.0" rel="noopener noreferrer"&gt;Changelog&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  More outstanding features
&lt;/h2&gt;

&lt;p&gt;Beside the new major release, our Taiga UI has been permanently improving. During the whole year, more than &lt;a href="https://github.com/Tinkoff/taiga-ui/issues?q=is%3Aissue+is%3Aclosed+closed%3A2022-01-01..2022-12-31+" rel="noopener noreferrer"&gt;500&lt;/a&gt; issues and more than &lt;a href="https://github.com/Tinkoff/taiga-ui/pulls?q=is%3Apr+is%3Aclosed+closed%3A2022-01-01..2022-12-31+" rel="noopener noreferrer"&gt;1500&lt;/a&gt; Pull Requests have been closed. The tasks were rather all over the place, I will tell you about the main directions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Localization&lt;/strong&gt;. Internationalization and localization of our libraries are very important to us. From the very get-go, Taiga UI has supported several languages, and thanks to the active help of the community, support has expanded to &lt;a href="https://github.com/Tinkoff/taiga-ui/tree/main/projects/i18n/languages" rel="noopener noreferrer"&gt;13 languages&lt;/a&gt; by the end of 2022. For the convenience of their dynamic switching at runtime, we have added &lt;a href="https://taiga-ui.dev/i18n/Dynamic_loader" rel="noopener noreferrer"&gt;some utilities&lt;/a&gt;. Nevertheless, translation only is not enough, countries have much more distinctive features.&lt;/p&gt;

&lt;p&gt;At the end of 2021, we set goals to expand the &lt;a href="https://en.wikipedia.org/wiki/Internationalization_and_localization" rel="noopener noreferrer"&gt;i18n&lt;/a&gt; of the product and added support for &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/direction" rel="noopener noreferrer"&gt;rtl‑languages&lt;/a&gt; for most inputs. Hands down, it wasn’t enough for us, so we continued to expand our localization capabilities in 2022. Now users of our library can set the desired &lt;a href="https://taiga-ui.dev/components/input-date#date-localization" rel="noopener noreferrer"&gt;date format&lt;/a&gt;, &lt;a href="https://taiga-ui.dev/utils/tokens#first-day-of-week" rel="noopener noreferrer"&gt;the first day of week&lt;/a&gt; (Sunday, Monday, or another day), which days in the calendar should be &lt;a href="https://taiga-ui.dev/components/calendar#color" rel="noopener noreferrer"&gt;marked as holidays/weekends&lt;/a&gt;, &lt;a href="https://taiga-ui.dev/utils/tokens#number-format" rel="noopener noreferrer"&gt;the format for displaying numbers&lt;/a&gt;, and other important little things – the devil is in the details, and Taiga UI is all about nuances and tiny aspects! &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;More component customization&lt;/strong&gt;. Many people love Angular for its flexible and convenient implementation of the Dependency Injection pattern. We try to use all power of DI in Taiga UI. We use &lt;a href="https://angular.io/guide/dependency-injection-in-action#injectiontoken-objects" rel="noopener noreferrer"&gt;Injection Tokens&lt;/a&gt; to customize the default behavior for the majority of our components. Such tokens have a naming consisting of the name of the component + the &lt;code&gt;OPTIONS&lt;/code&gt; postfix (for example, &lt;a href="https://taiga-ui.dev/editor/getting-started/DI_tokens" rel="noopener noreferrer"&gt;TUI_EDITOR_OPTIONS&lt;/a&gt;). In 2022, the list of such available tokens has expanded significantly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Projecting native outputs inside components&lt;/strong&gt;. In early 2022, we added the ability to project native inputs via &lt;code&gt;&amp;lt;ng-content /&amp;gt;&lt;/code&gt; inside Taiga UI input components. The concept is described in more detail in the &lt;a href="https://angular.io/guide/accessibility#using-containers-for-native-elements" rel="noopener noreferrer"&gt;"Best practices"&lt;/a&gt; section of the official Angular documentation.&lt;/p&gt;

&lt;p&gt;Let's see an example. Previously, to create an &lt;a href="https://taiga-ui.dev/components/input" rel="noopener noreferrer"&gt;Input&lt;/a&gt; with a &lt;a href="https://www.w3schools.com/tags/att_input_placeholder.asp" rel="noopener noreferrer"&gt;placeholder&lt;/a&gt; and a specific &lt;a href="https://www.w3schools.com/html/html_form_input_types.asp" rel="noopener noreferrer"&gt;type&lt;/a&gt;, you had to use this code:&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;tui-input&lt;/span&gt;
    &lt;span class="na"&gt;formControlName=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;
    &lt;span class="na"&gt;tuiTextfieldType=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;
    &lt;span class="na"&gt;tuiTextfieldExampleText=&lt;/span&gt;&lt;span class="s"&gt;"mail@mail.ru"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Type an email
&lt;span class="nt"&gt;&amp;lt;/tui-input&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now you can do it 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;tui-input&lt;/span&gt; &lt;span class="na"&gt;formControlName=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Type an email
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt;
        &lt;span class="na"&gt;tuiTextfield&lt;/span&gt;
        &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"mail@mail.ru"&lt;/span&gt;
        &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;
    &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/tui-input&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now users can set any available attribute on input and listen to any native event! For the user of our library, everything has become even more transparent. No more need to remember the names of our custom directives and find out which native attributes they map to. All this allowed us to drop a lot of unnecessary code from the &lt;a href="https://taiga-ui.dev/directives/textfield-controller" rel="noopener noreferrer"&gt;TextfieldController&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Custom notifications&lt;/strong&gt;. We have significantly refactored services for displaying notifications. Now the created abstractions make it easy to create custom notifications and use several types of notifications in the same project. The new &lt;a href="https://taiga-ui.dev/components/push" rel="noopener noreferrer"&gt;Push&lt;/a&gt; component is a good demonstration of this new feature.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Improvements for Editor&lt;/strong&gt;. All year we have been improving our new WYSIWYG, which is based on &lt;a href="https://github.com/ueberdosis/tiptap" rel="noopener noreferrer"&gt;Tiptap's&lt;/a&gt; Open Source solution. Here is a full list of new useful extensions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://taiga-ui.dev/components/accordion" rel="noopener noreferrer"&gt;Accordion&lt;/a&gt; support. Now you can &lt;a href="https://taiga-ui.dev/editor/getting-started/API?ngModel=%3Cdiv%20class%3D%22details-wrapper%20details-wrapper_rendered%22%3E%3Cdetails%20data-opened%3D%22true%22%3E%3Csummary%3E%3Cp%3ESpoiler%3C%2Fp%3E%3C%2Fsummary%3E%3Cdiv%20data-type%3D%22details-content%22%3E%3Cp%3ELuke,%20I%20Am%20Your%20Father%3C%2Fp%3E%3C%2Fdiv%3E%3C%2Fdetails%3E%3Cbutton%20class%3D%22details-arrow%22%3E%3C%2Fbutton%3E%3C%2Fdiv%3E%3Cp%3E%3C%2Fp%3E" rel="noopener noreferrer"&gt;add collapsible content&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;We added &lt;a href="https://taiga-ui.dev/editor/anchors" rel="noopener noreferrer"&gt;tools&lt;/a&gt; to create and edit anchor links.&lt;/li&gt;
&lt;li&gt;Users can now create &lt;a href="https://taiga-ui.dev/editor/nested-groups" rel="noopener noreferrer"&gt;nested groups/blocks&lt;/a&gt;. And also make them &lt;a href="https://taiga-ui.dev/editor/draggable-groups" rel="noopener noreferrer"&gt;draggable&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Our editor has image support: the user can upload an image, resize it, and control its position in the text. And now it is possible to &lt;a href="https://taiga-ui.dev/editor/preview-images" rel="noopener noreferrer"&gt;add previews&lt;/a&gt; of uploaded images.&lt;/li&gt;
&lt;li&gt;Dark Mode support.&lt;/li&gt;
&lt;li&gt;Stabilization of editing tables.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Component Harnesses&lt;/strong&gt;. With the help of the community, we began to actively develop a package for testing components with &lt;a href="https://material.angular.io/cdk/test-harnesses/overview" rel="noopener noreferrer"&gt;Harness&lt;/a&gt;. The first release of this package happened at the end of December.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Improving processes and DX&lt;/strong&gt;. In addition to permanently improving the codebase, we are also trying to improve the process of its development. It is important for us that all members of the core team and all external contributors have a high level of Developer Experience. After all, the more comfortable the conditions for the developer, the faster the delivery of new features. In 2022, we achieved new successes in this direction:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We changed the test framework for unit testing: from &lt;a href="https://jasmine.github.io" rel="noopener noreferrer"&gt;Jasmine&lt;/a&gt; + &lt;a href="https://karma-runner.github.io/latest/index.html" rel="noopener noreferrer"&gt;Karma&lt;/a&gt; to &lt;a href="https://jestjs.io" rel="noopener noreferrer"&gt;Jest&lt;/a&gt;. Now it takes only a few seconds to prepare the test environment before running tests. Moreover, the failed tests generate more comprehensive reports. For integration and screenshot testing, we use the 11th version of &lt;a href="https://www.cypress.io" rel="noopener noreferrer"&gt;Cypress&lt;/a&gt;, which allows us to test our components using the real DOM.&lt;/li&gt;
&lt;li&gt;We solved the problem of long execution of tests. We have splitted all tests into many separate parallel Github-jobs: now, when opening a PR, 14 jobs with Cypress tests and 12 jobs with unit tests are launched.&lt;/li&gt;
&lt;li&gt;We use the npm 8 features and &lt;a href="https://docs.npmjs.com/cli/v8/using-npm/workspaces" rel="noopener noreferrer"&gt;workspaces&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;We began to actively use &lt;a href="https://docs.github.com/en/issues/planning-and-tracking-with-projects" rel="noopener noreferrer"&gt;Github Projects&lt;/a&gt; for planning and tracking tasks.&lt;/li&gt;
&lt;li&gt;We added &lt;a href="https://github.com/apps/bundlemon" rel="noopener noreferrer"&gt;BundleMon&lt;/a&gt; and &lt;a href="https://github.com/apps/codecov" rel="noopener noreferrer"&gt;Codecov&lt;/a&gt; bots that allow you to control changes of the packages’ bundle-size and the code coverage by tests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Stackblitz workflow&lt;/strong&gt;. At the end of the year, we added the integration of our repository with a new feature from the StackBlitz team - &lt;a href="https://stackblitz.com/codeflow" rel="noopener noreferrer"&gt;Codeflow&lt;/a&gt;. Now our external contributors do not need to have a powerful computer with a pre-installed IDE. Now the developer can just click on the button from our root &lt;a href="https://github.com/Tinkoff/taiga-ui#-taiga-ui" rel="noopener noreferrer"&gt;README&lt;/a&gt;:&lt;/p&gt;

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

&lt;p&gt;And inside the browser an online IDE will be opened where you can serve demo application and open new PR. Now it becomes easier to contribute to Taiga UI!&lt;/p&gt;

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

&lt;p&gt;The year was rather challenging, but the Taiga UI team continues to develop the product. For the last several years we have achieved significant results, and much more is to come! It should be noted that the success of the library is partly attributed to the community, so, once again, thanks a lot for joining us in this adventure across the dense forest of Taiga! &lt;/p&gt;

&lt;p&gt;If you still did not get aboard, we can ensure you that by choosing our library for your projects, you will get a convenient and modern UI Kit with the confidence that support and development will not suddenly stop, not only because Taiga UI is actively used in hundreds of products of our company, but also because we do care about the faith of project and its community. &lt;/p&gt;

&lt;p&gt;Do you want to try it? Just write in console and enter the Taiga UI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng add taiga-ui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See you there! &lt;/p&gt;

</description>
      <category>architecture</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Sliders are like onions: three gradient layers of a single slider</title>
      <dc:creator>Barsukov Nikita</dc:creator>
      <pubDate>Tue, 05 Jul 2022 14:59:22 +0000</pubDate>
      <link>https://dev.to/angular/sliders-are-like-onions-three-gradient-layers-of-a-single-slider-1nia</link>
      <guid>https://dev.to/angular/sliders-are-like-onions-three-gradient-layers-of-a-single-slider-1nia</guid>
      <description>&lt;p&gt;Our library &lt;a href="https://github.com/Tinkoff/taiga-ui" rel="noopener noreferrer"&gt;Taiga UI&lt;/a&gt; contains hundreds of useful components, directives, and services. The code maintainability is a pivotal issue for our UI Kit library. That is why we always try to write as little code as possible and look for native solutions.&lt;/p&gt;

&lt;p&gt;In the article, I will tell you about such example – the development of Angular-component &lt;a href="https://taiga-ui.dev/components/slider" rel="noopener noreferrer"&gt;Slider&lt;/a&gt; using built-in browser tools and as few lines of JavaScript as possible. You will read about accessibility, an interesting solution with multi-layer gradient background, and even a bit about Change Detection in Angular.&lt;/p&gt;




&lt;h2&gt;
  
  
  It cannot be this hard, right?
&lt;/h2&gt;

&lt;p&gt;We have a task to develop a UI-element which helps users to select a number from an ordered range. Input consists of two parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;thumb&lt;/strong&gt; – the interactive element of the input. Users can move it horizontally to increase or decrease the selected number&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;track&lt;/strong&gt; – the space along which the thumb moves&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ux3o6mwfxzui5tikpfb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ux3o6mwfxzui5tikpfb.png" alt="Built-in slider in Chrome"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At first glance, the task seems simple. Listen to &lt;code&gt;MouseMove&lt;/code&gt; / &lt;code&gt;TouchMove&lt;/code&gt; events, calculate the distance how far from the beginning of the track they occurred, move the slider to there, and, finally, provide the user with a numerical value.&lt;/p&gt;

&lt;p&gt;It could even mistakenly seem that there is no sense in devoting the article to this subject because of its simplicity. However, we have to look into all the in-depth details before making such a conclusion. Let’s kick it off by getting acquainted with W3C documentation.&lt;/p&gt;

&lt;p&gt;W3C is a worldwide organization that maintains standards for the development of web applications and its primary goal is to make the Web accessible and understandable. To make things less abstract, imagine that one visits a website and opens a modal dialog. Which keyboard key would you intuitively like to press to close this dialog? It is an Esc button. In this sense, if a web application is developed by an experienced team, then the popup will close when you press this key.&lt;/p&gt;

&lt;p&gt;This example illustrates one the W3C principles, let’s dive deeper into &lt;a href="https://www.w3.org/WAI/ARIA/apg/patterns/slider/" rel="noopener noreferrer"&gt;W3C guidelines about sliders&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keyboard navigation&lt;/strong&gt;. Pressed &lt;code&gt;ArrowUp&lt;/code&gt;/&lt;code&gt;ArrowDown&lt;/code&gt; and &lt;code&gt;ArrowRight&lt;/code&gt;/&lt;code&gt;ArrowLeft&lt;/code&gt; keys should increase/decrease (respectively) the value of the slider by one step. Pressing &lt;code&gt;Home&lt;/code&gt;/&lt;code&gt;End&lt;/code&gt; should set the slider to the first/last allowed value in its range. Also, there are recommendations about keys &lt;code&gt;PageUp&lt;/code&gt;/&lt;code&gt;PageDown&lt;/code&gt;: they should increase/decrease the slider value by an amount larger than the step change made by &lt;code&gt;ArrowUp&lt;/code&gt;/&lt;code&gt;ArrowDown&lt;/code&gt;. If you develop your own slider, you should be ready to write 8 event handlers for the mentioned keys with different behaviors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Accessibility&lt;/strong&gt;. Any website can be used by people with disabilities, for instance, a user may experience issues using the keyboard and mouse. In such cases, voice-recognition and other assistive technologies are used. The user pronounces to the computer what it should do on the screen, however, for that the website should be correctly marked up.&lt;/p&gt;

&lt;p&gt;Moving back to our task, let’s see the requirements for an accessible slider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The element serving as the focusable slider control has &lt;code&gt;role="slider"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The slider element has the &lt;code&gt;aria-valuenow&lt;/code&gt; property set to the current value of the slider&lt;/li&gt;
&lt;li&gt;The slider element has the &lt;code&gt;aria-valuemin&lt;/code&gt; / &lt;code&gt;aria-valuemax&lt;/code&gt; property set to the minimum/maximum allowed value of the slider&lt;/li&gt;
&lt;li&gt;If the value of &lt;code&gt;aria-valuenow&lt;/code&gt; is not user-friendly, the &lt;code&gt;aria-valuetext&lt;/code&gt; property is set to a string that makes the slider value understandable. A simple example from the W3C’s documentation: if you decide to use a slider to select the days of the week (from the first to the seventh), don't forget to provide the slider with a text description of the selected value (e.g., "Monday")&lt;/li&gt;
&lt;li&gt;Finally, we should always keep &lt;code&gt;aria-label&lt;/code&gt; in mind!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Possibility to configure the slider step&lt;/strong&gt;. For instance, a developer can restrict to selecting only integer values. Also, they may allow the user to input values with &lt;code&gt;0.0001&lt;/code&gt;-precision.&lt;/p&gt;

&lt;p&gt;Considering all the nuances and accessibility principles might be perceived as tons of extra work, that is why everything is not as simple as it might seem at first glance. &lt;/p&gt;

&lt;p&gt;But the browser has already done it! There is a built-in &lt;code&gt;&amp;lt;input type="range" /&amp;gt;&lt;/code&gt; slider that follows all mentioned standards. It is not surprising to anyone that such a built-in solution is not fancy, and even looks different in each browser. Therefore, I will show you how to customize the built-in slider to one’s needs to ensure cross-browser support.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We will write the component for Angular. But the solution contains so little Javascript code that you can easily transfer it to your favorite framework.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Anatomy of &lt;code&gt;&amp;lt;input type="range" /&amp;gt;&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;If you enable the shadow DOM in your DevTools (e.g., in Chrome’s settings, one can find option “Show user agent shadow DOM”), you can notice that the built-in &lt;code&gt;&amp;lt;input type="range" /&amp;gt;&lt;/code&gt; is not a single tag. It contains other tags inside (but we did not add them!). Also, the internal structure of nested tags may vary in different browsers. Somewhere you can find a flat structure with 3 &lt;code&gt;div&lt;/code&gt;-containers, and somewhere – additional depth of the structure.&lt;/p&gt;

&lt;p&gt;Each browser engine has a different approach to writing its own slider, so they all have a different structure. But the basic concepts are the same: there is always a tag with a slider track and a container with a thumb. You just need to know how to access these nested tags.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can't access the HTML tags inside an &lt;code&gt;&amp;lt;input /&amp;gt;&lt;/code&gt; via &lt;a href="https://www.w3schools.com/css/css_combinators.asp" rel="noopener noreferrer"&gt;combinator CSS selector&lt;/a&gt; (for instance, &lt;code&gt;input[type="range"] &amp;gt; div&lt;/code&gt;). All slider markup is encapsulated inside the shadow DOM and is not accessible from the outside. But browsers provide API (pseudo-classes) via which you can reach the necessary tags.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;All browsers with the WebKit/Blink engines (e.g., Safari and Chrome) have this anatomy:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpi3di8h4rs1azh5a21ry.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpi3di8h4rs1azh5a21ry.png" alt="Structure of slider in WebKit/Blink browsers"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Firefox gives more opportunities for customization: besides the pseudo-classes for the track and the thumb, there is an additional pseudo-class to customize the progress indicator of the slider.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdjvpqdviagv1guxc9xfa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdjvpqdviagv1guxc9xfa.png" alt="Structure of slider in Firefox"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's all you need to know to write your own simple slider. However, if you need more flexibility, something more complex than tag coloring, you will face some problems. I'll tell you about possible problems and how to deal with them.&lt;/p&gt;

&lt;h2&gt;
  
  
  This’ll be fun: writing a basic slider
&lt;/h2&gt;

&lt;p&gt;Firstly, let's use previous information to customize the basic version of our slider.&lt;/p&gt;

&lt;p&gt;Declare some &lt;a href="https://lesscss.org/features/#variables-feature" rel="noopener noreferrer"&gt;less-variables&lt;/a&gt; for self-documentation of the code:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Declare &lt;a href="https://lesscss.org/features/#mixins-feature" rel="noopener noreferrer"&gt;less-mixin&lt;/a&gt; to customize track of the slider. It will be simple for now, but it will be considerably extended in the following sections.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;We also need mixin to customize the thumb of the slider:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;If you don't quite understand how the &lt;code&gt;&amp;amp;&lt;/code&gt;-symbol works, I advise you to read this &lt;a href="https://lesscss.org/features/#parent-selectors-feature" rel="noopener noreferrer"&gt;Less-documentation section&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Finally, let’s create a special mixin only for webkit browsers. We need it if the slider’s thumb is larger/smaller than the height of the slider track. Webkit browsers don’t align it to the center. Multiple ways to vertically center the content won't help here, but the problem can be solved in this way:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Now you can create an Angular–component. Our component has no additional layout, but it has an attribute selector with the native element &lt;code&gt;&amp;lt;input type="range" /&amp;gt;&lt;/code&gt;. Using attribute selector is a good practice to augment the behavior of native elements. Official Angular documentation &lt;a href="https://angular.io/guide/accessibility#augmenting-native-elements" rel="noopener noreferrer"&gt;claims&lt;/a&gt;: "When authoring Angular components, you should re-use these native elements directly when possible, rather than re-implementing well-supported behaviors". You can find many such examples in our &lt;a href="https://github.com/Tinkoff/taiga-ui" rel="noopener noreferrer"&gt;Taiga UI library&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The component uses a change-detection strategy &lt;code&gt;OnPush&lt;/code&gt;. If you don’t know it or hesitate to use it, I recommend reading the article of my colleague:&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="https://medium.com/angularwave/onpush-your-new-default-ba3fd5bc9f6e" class="ltag__link__link" rel="noopener noreferrer"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afill%3A88%3A88%2F1%2AnHIUPd5I-BaSYG8u95N4Kg.jpeg" alt="Alexander Inkin"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://medium.com/angularwave/onpush-your-new-default-ba3fd5bc9f6e" class="ltag__link__link" rel="noopener noreferrer"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;OnPush — your new Default | by Alex Inkin | AngularWave&lt;/h2&gt;
      &lt;h3&gt;Alexander Inkin ・ &lt;time&gt;Aug 5, 2021&lt;/time&gt; ・ 
      &lt;div class="ltag__link__servicename"&gt;
        &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fmedium-f709f79cf29704f9f4c2a83f950b2964e95007a3e311b77f686915c71574fef2.svg" alt="Medium Logo"&gt;
        Medium
      &lt;/div&gt;
    &lt;/h3&gt;
&lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;Typescript-file of our component contains:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Our component's selector forces the user to specify the input type (type="range"). You could suggest improving the developer experience and set this static property through &lt;code&gt;host&lt;/code&gt;-property in the component's metadata. But unfortunately, this breaks the logic of the built-in &lt;a href="https://github.com/angular/angular/blob/main/packages/forms/src/directives/range_value_accessor.ts#L45" rel="noopener noreferrer"&gt;controlValueAccessor from the Angular team&lt;/a&gt;, which is applied via the &lt;code&gt;input[type=range]&lt;/code&gt; CSS selector.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the Less file, we apply all previously created mixins to the &lt;code&gt;:host&lt;/code&gt; – selector (just a reminder: in this case, it is &lt;code&gt;&amp;lt;input type="range" /&amp;gt;&lt;/code&gt;– element):&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Voilà! We have got a basic cross-browser solution for customizing the built-in slider. But there are more nuances that are currently overlooked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Webkit customization problems with the progress of the slider
&lt;/h2&gt;

&lt;p&gt;Firefox takes care of the developer's desire to customize the progress indicator of the slider – the &lt;code&gt;moz-range-progress&lt;/code&gt; pseudo-class. Unfortunately, there is no such functionality for WebKit/Blink browsers. But &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/gradient/linear-gradient" rel="noopener noreferrer"&gt;linear gradients&lt;/a&gt; can solve this problem.&lt;/p&gt;

&lt;p&gt;Previously, we just filled the entire slider track in gray. And now, we will fill part of the track in the color of progress, and leave the rest of it gray.&lt;/p&gt;

&lt;p&gt;We add some simple math to the class of the component. It will calculate how many percents the slider are filled, and save this value to the &lt;code&gt;--slider-fill-percentage&lt;/code&gt; &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties" rel="noopener noreferrer"&gt;CSS-variable&lt;/a&gt;:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Now, we need to improve the previously created mixin &lt;code&gt;customize-track&lt;/code&gt;:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Let's try the resulting component in action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"range"&lt;/span&gt; &lt;span class="na"&gt;tuiSlider&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"30"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We're using &lt;code&gt;changeDetectionStrategy.OnPush&lt;/code&gt;, so it's expected that the slider doesn’t recalculate the value of the &lt;code&gt;--slider-fill-percentage&lt;/code&gt; CSS-variable while the user moves the slider's thumb. We need to tell Angular when to run the change detection. Achieving this is simple: we need to trigger change detection when the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event" rel="noopener noreferrer"&gt;InputEvent&lt;/a&gt; fires. We set an empty event handler in the component metadata:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Now, the value of the &lt;code&gt;--slider-fill-percentage&lt;/code&gt; CSS-variable is recalculated when the user interacts with the slider.&lt;/p&gt;

&lt;p&gt;One problem less, and now our slider is &lt;strong&gt;completely&lt;/strong&gt; identical in all modern browsers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding ticks via multi-layer gradient
&lt;/h2&gt;

&lt;p&gt;We have a working solution. Let's imagine that we wanted to use a slider with the following combination of native attributes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"range"&lt;/span&gt; &lt;span class="na"&gt;tuiSlider&lt;/span&gt; &lt;span class="na"&gt;min=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; &lt;span class="na"&gt;max=&lt;/span&gt;&lt;span class="s"&gt;"100"&lt;/span&gt; &lt;span class="na"&gt;step=&lt;/span&gt;&lt;span class="s"&gt;"20"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This combination means that the user can only move the slider by a step that is a multiple of 20. Users can only select five values in the range, or in other words, the slider consists of five segments. A popular practice for such a case is to highlight segments visually through ticks/marks on the slider track.&lt;/p&gt;

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

&lt;p&gt;We can try to achieve this via additional (pseudo-)elements inside the input. But only webkit browsers support this behavior, because it's not a commonly accepted standard. Another option is to try to implement the ticks through a gradient, but in the last chapter, a gradient was already used to implement a progress indicator for webkit browsers (so the place seems to be already taken).&lt;/p&gt;

&lt;p&gt;No worries! There is a solution: gradients can be stacked on each other in several layers. We can create a new layer using &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/gradient/repeating-linear-gradient" rel="noopener noreferrer"&gt;repeating-linear-gradient&lt;/a&gt; and apply it on top of the previous one.&lt;/p&gt;

&lt;p&gt;Firstly, let's add these lines to the component code:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;We have added new input-property &lt;code&gt;segments&lt;/code&gt;, with which the developer can set the number of visual segments to be shown on the slider track. And the new getter of the component performs a simple math operation – it calculates how many percent of the total length each segment should occupy and saves the result in the &lt;code&gt;--slider-segment-width&lt;/code&gt; CSS-variable.&lt;/p&gt;

&lt;p&gt;It remains to upgrade the &lt;code&gt;.customize-track()&lt;/code&gt; mixin. To apply several gradient layers to the same background, just separate them by commas in the &lt;code&gt;background-image&lt;/code&gt; property. For example:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faghhvk5ttz0vfi96jbvw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faghhvk5ttz0vfi96jbvw.png" alt="An illustration of how multiple gradient layers are stacked on top of each other"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The main thing is not to get confused in what order the layers stack on each other. The first specified gradient will be layered on top of all others, and the last one will become the bottom layer of the background. In other words, the stacking of layers on top of each other begins at the end of the list enumeration.&lt;/p&gt;

&lt;p&gt;All other &lt;code&gt;background-*&lt;/code&gt; properties have a similar behavior. You can specify only one set of parameters for all layers, or you can list the parameters separated by commas (in the same order as in the &lt;code&gt;background-image&lt;/code&gt; property) to set your own values for each layer.&lt;/p&gt;

&lt;p&gt;Upgraded version of mixin &lt;code&gt;customize-track&lt;/code&gt;:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;An important feature of native sliders is that when the user moves the thumb to the start/end of the track, the thumb touches the end of the track not with its center, but with its edge. So, the thumb of the slider never goes beyond the visual boundaries of the slider track. Therefore, the first mark does not start at the very beginning of the track, but with a shift, which allows it to be in the center of the thumb (when it is in the leftmost position), and also correctly places the rest of the marks.&lt;/p&gt;

&lt;p&gt;In addition to the &lt;code&gt;background-image&lt;/code&gt; layers, we also set a value for &lt;code&gt;background-color&lt;/code&gt;. The logic here is as follows: &lt;code&gt;background-color&lt;/code&gt; acts as the last fallback layer. The entire surface of the track, which is not covered by any of the layers, will be filled in the color of this property’s value.&lt;/p&gt;

&lt;p&gt;That's all. This solution is a good illustration of how sometimes a frontend-task can be solved without additional nested HTML tags.&lt;/p&gt;

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

&lt;p&gt;I reproduced the final version of the slider in StackBlitz example:&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://stackblitz.com/edit/ng-slider?embed=1&amp;amp;file=src/slider/slider.component.ts&amp;amp;hideNavigation=1" width="100%" height="500"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://github.com/Tinkoff/taiga-ui" rel="noopener noreferrer"&gt;Taiga UI library&lt;/a&gt;, we use exactly the same solution with minor improvements. On the &lt;a href="https://taiga-ui.dev/components/slider" rel="noopener noreferrer"&gt;showcase of the Slider component&lt;/a&gt;, you can see examples of its usage.&lt;/p&gt;

&lt;p&gt;It is not always necessary to invent your own solutions – sometimes, we can re-use native alternatives with little modifications. When you ignore a built-in solution, you expand your codebase, increasing the complexity of its support. No wonder one modern wisdom says: the code easiest to maintain is the code that was never written.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>typescript</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
