<?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: Alex Inkin</title>
    <description>The latest articles on DEV Community by Alex Inkin (@waterplea).</description>
    <link>https://dev.to/waterplea</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%2F442490%2F440f898d-bfb2-4d96-9492-45ca5ccc6416.jpg</url>
      <title>DEV Community: Alex Inkin</title>
      <link>https://dev.to/waterplea</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/waterplea"/>
    <language>en</language>
    <item>
      <title>CSS Challenges for 200 IQ</title>
      <dc:creator>Alex Inkin</dc:creator>
      <pubDate>Wed, 05 Aug 2026 06:53:35 +0000</pubDate>
      <link>https://dev.to/waterplea/css-challenges-for-200-iq-ghl</link>
      <guid>https://dev.to/waterplea/css-challenges-for-200-iq-ghl</guid>
      <description>&lt;p&gt;Do you ever get that feeling when you’re working on a task, hit a wall with some problem, and something inside you whispers that there has to be a solution? When it seems like all is lost, like you’ve run into a fundamental limit of reality, but your refusal to accept it keeps driving you deeper into spec docs, 10-year-old GitHub threads, and articles from giants who’ve already blazed this trail and shared their findings? And then, after hours of intense brain-grinding, you add that final line of code, refresh the page, and there it is — the exact result you wanted, staring back at you from the screen? That rush of success is probably familiar to every engineer in some form or another. In those moments, I always want to share the win with my colleagues and, if it could help others, write an article about it. In this post, I’ve collected 3 such cases from our work where we came up with solutions that, as far as I know, are pretty unique and haven’t been fully documented before. I invite you to share in the joy of discovering a solution that seemed impossible!&lt;/p&gt;

&lt;h2&gt;
  
  
  Fixed inside a Scroll Container
&lt;/h2&gt;

&lt;p&gt;For a warm-up, let’s take an easier task. One of my most popular &lt;a href="https://codepen.io/waterplea" rel="noopener noreferrer"&gt;CodePens&lt;/a&gt; is an example of a fixed block inside a scrolling container. People find it via Stack Overflow answers, so it’s an in-demand problem, so it might come in handy for you too.&lt;/p&gt;

&lt;p&gt;I’ve been working on an Angular component library called &lt;a href="https://taiga-ui.dev" rel="noopener noreferrer"&gt;Taiga UI&lt;/a&gt; for many years. Everything I’ll talk about in this article comes from there, but that’s just the backstory. We won’t need Angular or any of its specifics here. We’re talking pure CSS. Our library uses a custom scrollbar. While modern browsers &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Scrollbars_styling" rel="noopener noreferrer"&gt;let you tweak its appearance a bit&lt;/a&gt;, for full control over behavior and visuals, we need to place our own elements inside the container to act as the scrollbar. But how do you do that when absolutely positioned elements fly to the top on scroll, and fixed-position ones are pinned to the viewport?&lt;/p&gt;

&lt;p&gt;Experienced devs will immediately think of &lt;code&gt;position: sticky&lt;/code&gt;, but those elements take up space and only stick once you’ve scrolled the container past them. We’d need to compensate for their height somehow, but using negative percentage margins references the horizontal dimensions, when we actually need vertical ones. And that’s where the almost-forgotten in this post-Flexbox era &lt;code&gt;float&lt;/code&gt; property comes to the rescue!&lt;/p&gt;

&lt;p&gt;We want a block that covers the entire scrolling container while staying fixed during scroll. To do this, set &lt;code&gt;inset: 0&lt;/code&gt; and &lt;code&gt;width: 100%&lt;/code&gt; / &lt;code&gt;height: 100%&lt;/code&gt;. As you recall, percentage margins are based on the element’s horizontal size — and that’ll work perfectly here. Add float: left to make it “step aside,” and &lt;code&gt;margin-right: -100%&lt;/code&gt; clears all the space it occupied for the rest of the container’s content:&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;.overlay&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="n"&gt;sticky&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;inset&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="nl"&gt;height&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;width&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;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;left&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
  &lt;span class="nl"&gt;margin-right&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;pointer-events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/waterplea/embed/JjjMXzR?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Now you’ve got a container that acts fixed inside a scrollable block, and you can easily place anything in it — like buttons or toasts — without worrying about the main content.&lt;/p&gt;

&lt;h2&gt;
  
  
  Parameters for SVG
&lt;/h2&gt;

&lt;p&gt;Now let’s pick something trickier. For displaying colorable icons, we use &lt;code&gt;mask-image&lt;/code&gt;: fill the block’s background with the desired color, then cut it out along the icon’s shape using a &lt;strong&gt;mask&lt;/strong&gt;. My colleague Nikita already wrote about all the cool things you can do with CSS masks — highly recommend &lt;a href="https://javascript.plainenglish.io/css-mask-powers-236a73c4714c" rel="noopener noreferrer"&gt;reading it&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It’s a super simple approach that avoids DOM manipulation (and even nesting with &lt;code&gt;::before&lt;/code&gt;/&lt;code&gt;::after&lt;/code&gt;), leverages the browser’s built-in caching, and works with cross-domain CDNs. Beauty everywhere — except you can’t control line thickness, since there’s no way to pass stroke-width. Or so I thought.&lt;/p&gt;

&lt;p&gt;When asked yet again if we could tweak the pre-set stroke width in icons, I went to check how the &lt;a href="https://drafts.csswg.org/css-link-params-1/" rel="noopener noreferrer"&gt;spec for passing parameters to SVG&lt;/a&gt; is doing. Naturally, I didn’t remember where to find it or even its exact name, and while searching, I stumbled on an article with a genius idea:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://kizu.dev/svg-linked-parameters-workaround" rel="noopener noreferrer"&gt;https://kizu.dev/svg-linked-parameters-workaround&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’re too lazy to read it, the gist is: when setting viewBox and centering the image, you can make the mask height 100% but width any value from 100% and up, and the image looks identical. So what? Well, inside the SVG file’s own styles, those dimensions affect 100vw and 100vh. This lets you encode info via aspect ratio without changing the visual appearance. All that’s left is layering on a couple of &lt;code&gt;calc()&lt;/code&gt; expressions so users can set line thickness via an intuitive — &lt;code&gt;stroke-width&lt;/code&gt; variable:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;stroke-width=”calc((100vw — 100vh) / 10)”&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.icon&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;mask-image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url(...)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;mask-repeat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;no-repeat&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;mask-position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;mask-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100%&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt; &lt;span class="err"&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;--stroke-width&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the division and multiplication by 10 — this allows for subpixel thicknesses, like 1.5px.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/waterplea/embed/XJjjGNL?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Perfect Shrinkwrap
&lt;/h2&gt;

&lt;p&gt;Let’s ramp it up. New features in modern CSS have solved one of the oldest UI problems — one that previously had no solution: tight wrapping, also known as shrink wrapping. It’s best shown visually:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpphkdtvhjdr8zlcodvt1.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpphkdtvhjdr8zlcodvt1.png" alt="Example of bad text wrapping" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A container with text expands to fit as much text as possible, but once it hits max width, words wrap to new lines. This creates useless empty space, especially noticeable if there are elements after the text:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftyqge1zz12bvhv85mat9.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftyqge1zz12bvhv85mat9.png" alt="Default multi-line toast display vs. how we’d like it" width="799" height="316"&gt;&lt;/a&gt;&lt;/p&gt;
Default multi-line toast display vs. how we’d like it






&lt;p&gt;The solution, however, is pretty intricate and wasn’t easy to discover. It started with a clever technique for calculating an element’s size in pure CSS using &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Scroll-driven_animations" rel="noopener noreferrer"&gt;scroll-driven animations&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://frontendmasters.com/blog/how-to-get-the-width-height-of-any-element-in-only-css" rel="noopener noreferrer"&gt;https://frontendmasters.com/blog/how-to-get-the-width-height-of-any-element-in-only-css&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Again, for those skipping the full article: CSS now lets you track an element’s position inside its container with a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/view-timeline" rel="noopener noreferrer"&gt;view timeline&lt;/a&gt;. Designed for changing styles based on scroll position, it also lets us store the container’s size in a CSS variable. Roughly: animation progress via view timeline gives the percentage of the container occupied by the tracked element. Set it to a fixed size like 1px, and simple math reveals the container size.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6fbrnka4qqfpdczfs8iz.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6fbrnka4qqfpdczfs8iz.png" alt="Illustration from the article above" width="799" height="267"&gt;&lt;/a&gt;&lt;/p&gt;
Illustration from the article above



&lt;p&gt;&lt;br&gt;&lt;br&gt;
This unlocks tons of new tricks — already working in Chrome and Safari, coming soon to Firefox. With container and inline text (that wraps) sizes calculable, we can find the unused space left. Subtract that gap from the container’s max width and its right margin. This keeps measured elements stable, avoiding an infinite shrink/expand loop:&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;.inline&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* Measuring text */&lt;/span&gt;
  &lt;span class="py"&gt;view-timeline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--inner-timeline&lt;/span&gt; &lt;span class="nb"&gt;inline&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.inline&lt;/span&gt;&lt;span class="nd"&gt;::before&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;""&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;1px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;margin-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="p"&gt;;&lt;/span&gt;

  &lt;span class="c"&gt;/* Measuring container */&lt;/span&gt;
  &lt;span class="py"&gt;view-timeline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--outer-timeline&lt;/span&gt; &lt;span class="nb"&gt;inline&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;outer&lt;/span&gt; &lt;span class="n"&gt;linear&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inner&lt;/span&gt; &lt;span class="n"&gt;linear&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;animation-range&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt; &lt;span class="n"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;animation-timeline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--outer-timeline&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;--inner-timeline&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
  &lt;span class="py"&gt;timeline-scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--outer-timeline&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;--inner-timeline&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c"&gt;/* Measuring the gap */&lt;/span&gt;
  &lt;span class="py"&gt;--delta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-1px&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="n"&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;--outer&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="err"&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;--inner&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="c"&gt;/* Shrinking the width */&lt;/span&gt;
  &lt;span class="py"&gt;max-inline-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;15rem&lt;/span&gt; &lt;span class="err"&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;--delta&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.block&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="c"&gt;/* Compensating the gap */&lt;/span&gt;
  &lt;span class="py"&gt;margin-inline-end&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;--delta&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 get extra space compensated as the result:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/waterplea/embed/GgjoBxe?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

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

&lt;p&gt;The world around us is changing at crazy speed. AI is breathing down our necks from all sides, with cries of “run just to stay in place” and all that jazz. But I love my work, do it with real pleasure, and eagerly await the next puzzle that brings the joy of discovery and overcoming the impossible. I hope you’ve shared in it today, even though I know most of us front-end devs aren’t huge CSS fans. Maybe someday this article will help some AI tackle these challenges, but the effort, the grind, the brain-wracking — that’s ours, and I’ll keep cherishing it.&lt;/p&gt;

</description>
      <category>css</category>
      <category>webdev</category>
      <category>html</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Maskito: a Holy Grail of input masking</title>
      <dc:creator>Alex Inkin</dc:creator>
      <pubDate>Sun, 02 Jul 2023 12:10:29 +0000</pubDate>
      <link>https://dev.to/waterplea/maskito-a-holy-grail-of-input-masking-3gn4</link>
      <guid>https://dev.to/waterplea/maskito-a-holy-grail-of-input-masking-3gn4</guid>
      <description>&lt;p&gt;If you have ever worked on complex user forms, you definitely needed a masking solution. You want your numbers to be properly formatted so it is easy for users to make sure they have entered the correct amount at a glance. Or compare credit card number with an actual card, enter their phone or birthday or an appointment time. Masking has a lot of use cases on the web but to find a good solution is not an easy task. A lot of the time masking libraries can cause frustration for both developers and users. Common issues are jumping caret, troubles with added symbols like dashes or brackets, browser autofill or errors in server side rendering and convoluted API.&lt;/p&gt;

&lt;p&gt;We were no strangers to these troubles and at some point we decided to dedicate serious resources to our own solution. Today I would like to present it to you and explain why it is worth your attention.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Maskito and why should you care?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/Tinkoff/maskito" rel="noopener noreferrer"&gt;Maskito&lt;/a&gt; is a set of libraries for input masking with focus on both UX and DX. You can check out our &lt;a href="https://tinkoff.github.io/maskito" rel="noopener noreferrer"&gt;extensive documentation portal&lt;/a&gt;. There you can find plenty of examples, as well as a detailed walkthrough of the API. The “Getting started” section will explain the basics and will quickly get you familiar with concepts and use cases. You can also read a &lt;a href="https://medium.com/its-tinkoff/maskito-is-a-new-collection-of-libraries-for-text-field-masking-f64ec71951df" rel="noopener noreferrer"&gt;more technical article&lt;/a&gt; by Maskito lead developer &lt;a href="https://twitter.com/nsbarsukov" rel="noopener noreferrer"&gt;Nikita Barsukov&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Of course, Maskito is not the only solution you can find. But I believe there are certain things setting it apart from most alternatives available right now. Hence the ambitious title of this article. Let’s briefly explore why you should consider Maskito if you are looking for a masking solution for your project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Backed by enterprise
&lt;/h3&gt;

&lt;p&gt;Unlike many open source libraries that are maintained by individuals, Maskito is a product developed and used by one of the world’s leading fintech companies. We are seasoned open source authors getting paid for what we do so you can rely on timely support and continuous development.&lt;/p&gt;

&lt;p&gt;Our team already has a stock of welcomed projects, especially in the Angular ecosystem, such as &lt;a href="https://github.com/Tinkoff/taiga-ui" rel="noopener noreferrer"&gt;Taiga UI&lt;/a&gt;, &lt;a href="https://github.com/Tinkoff/ng-polymorpheus" rel="noopener noreferrer"&gt;ng-polymorpheus&lt;/a&gt; and &lt;a href="https://github.com/Tinkoff/ng-web-apis" rel="noopener noreferrer"&gt;Web APIs for Angular&lt;/a&gt; to name a few. We take them very seriously because they are not pet projects or hobbies (although we love them as if they were). Therefore you can expect our documentation to be easy to navigate, examples to be plentiful and communication to go effortlessly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Framework agnostic
&lt;/h3&gt;

&lt;p&gt;Whether you use React, Vue or Angular for your product or you rely on some other framework or even vanilla JavaScript — Maskito got you covered. Our main package is dependency free and can be used in any frontend application. And so are mask presets that we will cover below.&lt;/p&gt;

&lt;p&gt;At the time of writing we have dedicated packages to use Maskito in React, Vue and Angular with comfort. Since the public API is rather succinct, it would be easy to expand support to other popular frameworks. Actual masking configuration is the same and can be shared across different projects and architectures with ease.&lt;/p&gt;

&lt;h3&gt;
  
  
  Environment agnostic
&lt;/h3&gt;

&lt;p&gt;Maskito works in all modern browsers regardless of the environment you run it in. You can be using Web Components with Shadow DOM or employ SSR to improve your time to interactive and SEO — your inputs will not give you trouble by trying to access ‘window’ under nodejs or messing with encapsulated focus. It will also work properly on touch devices and virtual keyboards on both inputs and textarea elements. Clipboard interaction, undo/redo, text drag and drop or browser autofill are also covered by Maskito.&lt;/p&gt;

&lt;p&gt;Our goal is to create a go-to masking solution for the web and the web is as diverse as it gets. If you face any issues with some specific configuration, please do not hesitate to &lt;a href="https://github.com/Tinkoff/maskito/issues/new/choose" rel="noopener noreferrer"&gt;raise an issue&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quality code
&lt;/h3&gt;

&lt;p&gt;Maskito is maintained by an experienced lead developer and a team of contributors with a history of delivering widely used versatile solutions and open source libraries. Multiple Google Developer Experts curate the project and make sure architectural decisions are well thought through and make sense in the long run.&lt;/p&gt;

&lt;p&gt;All code is written in strict TypeScript and covered by tons of e2e tests and community contributions are thoroughly reviewed. We have tightly configured linters and automated pipelines we perfected for years on other projects to make sure technical flaws do not make it to the release. We use it in a multi million users business product. It is in our best interest that the caret does not jump all over your input fields.&lt;/p&gt;

&lt;h3&gt;
  
  
  Not just us
&lt;/h3&gt;

&lt;p&gt;While we of course trust our solution and are happy with it, you might be wondering if it would suit your particular needs. We have a wide array of products with different browser and device usage, different specs and requirements. Our team consists of engineers that specialize in generalized solutions and flexible APIs. So we know there’s an endless pool of use cases and we are prepared.&lt;/p&gt;

&lt;p&gt;But it is not just us — &lt;a href="https://ionicframework.com/" rel="noopener noreferrer"&gt;Ionic Framework&lt;/a&gt;, a multi platform UI solution with nearly 50k stars on GitHub has adopted Maskito as its official masking &lt;a href="https://ionic.io/blog/announcing-ionic-v7-1" rel="noopener noreferrer"&gt;library recommendation&lt;/a&gt;. Their team did an investigation of existing alternatives and considered an option to develop their own mask. They decided Maskito would satisfy the needs of their extremely varied user base and added examples of masking their components to the &lt;a href="https://ionicframework.com/docs/api/input#input-masking" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ready to use
&lt;/h3&gt;

&lt;p&gt;Maskito comes with a dedicated package of ready-to-use masks. We maintain a huge components library that has number formatting, credit cards, phones, dates etc. It is used in dozens of applications across our ecosystem, where additional masking is required for zip codes, IP addresses, serial numbers and so on. Once they are implemented and properly tested, all generally applicable cases are extracted and reused.&lt;/p&gt;

&lt;p&gt;This is an ever growing collection of presets that you can take as-is and be sure they behave properly. You can contribute to it or raise a feature request for a particular solution at our &lt;a href="https://github.com/Tinkoff/maskito" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;. This makes Maskito as tiny or as comprehensive as you need it to be. Core package is only about 3KB gzip and every piece of presets is tree shakable so your bundle is in safe hands.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Maskito is free for both personal and commercial use and is released under Apache 2.0 license. I am really proud of what the team is doing and believe it to be the best solution for the task. I hope it can help you bring your ideas to life in a smooth way both for your users and developers. Check Maskito out and if you like it, support us with a star and help us spread the word. Happy masking everyone!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>frontend</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Taiga UI: A year in Open Source</title>
      <dc:creator>Alex Inkin</dc:creator>
      <pubDate>Fri, 24 Dec 2021 13:28:19 +0000</pubDate>
      <link>https://dev.to/angular/taiga-ui-a-year-in-open-source-416l</link>
      <guid>https://dev.to/angular/taiga-ui-a-year-in-open-source-416l</guid>
      <description>&lt;p&gt;&lt;a href="https://taiga-ui.dev" rel="noopener noreferrer"&gt;Taiga UI&lt;/a&gt; is a huge Angular components library. We at &lt;a href="https://www.tinkoff.ru/eng" rel="noopener noreferrer"&gt;Tinkoff&lt;/a&gt; were developing it closed-source for a couple of years before finally going public with a second major version. It’s been a year since our open source release and today I want to look back at this part of our journey and lay down some plans for 2022. Let’s go!&lt;/p&gt;

&lt;h1&gt;
  
  
  Open source
&lt;/h1&gt;

&lt;p&gt;Over this time the community started to build up. One benefit for going &lt;a href="https://github.com/TinkoffCreditSystems/taiga-ui" rel="noopener noreferrer"&gt;open source on GitHub&lt;/a&gt; is contribution made simpler even for colleagues from different projects. The environment is familiar, no extra authentication or VPN required and the pipeline is clear. &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%2F54ae293y2mv6yqvz1671.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%2F54ae293y2mv6yqvz1671.png" alt=" " width="512" height="318"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We also spent quite some time to improve our processes utilizing many different tools to help us ship new versions safer and faster. &lt;/p&gt;

&lt;p&gt;Taiga UI is a monorepo that includes several libraries and a demo application that we deploy using GitHub Pages. Now it is managed by &lt;a href="https://nx.dev/" rel="noopener noreferrer"&gt;NX tools&lt;/a&gt; that provide a lot of useful commands and increase speed of building and testing at both local development and CI/CD pipelines. If you plan on working with monorepos you should definitely try NX!&lt;/p&gt;

&lt;p&gt;Whenever a Pull Request is created we need to be able to quickly checkout the changes. Reading code diff is great, but sometimes you just need to tinker with the new version, test it on mobile, different browsers and OS. Cloud services are perfect for this case, they allow you to deploy the code temporarily and access it with a link from any device. We chose &lt;a href="https://firebase.google.com/" rel="noopener noreferrer"&gt;Firebase&lt;/a&gt; to host it for us and a Github action posts a link to the deployment as a comment in the Pull Request. It works like a charm and speeds up code reviews a lot. Read &lt;a href="https://firebase.google.com/docs/hosting/github-integration" rel="noopener noreferrer"&gt;this article&lt;/a&gt; to set it up on your repository!&lt;/p&gt;

&lt;p&gt;Moreover, every PR runs a set of &lt;a href="https://www.cypress.io/" rel="noopener noreferrer"&gt;Cypress&lt;/a&gt; screenshot tests that iterates over the demo portal and compares current components with their reference from the main branch. Cypress has a commercial solution where you can easily access failed screenshots through a dashboard. But we created a little &lt;a href="https://github.com/TinkoffCreditSystems/argus" rel="noopener noreferrer"&gt;Github bot&lt;/a&gt; that stores failed screenshots in a temporary branch and posts them to the PR as a comment, updating it on every push. This makes identifying problems with the UI library fast and easy. We opensourced this bot so you can use it too, read &lt;a href="https://medium.com/its-tinkoff/bots-should-work-developers-should-think-writing-github-app-with-node-js-2e8eb049d7e4" rel="noopener noreferrer"&gt;this article&lt;/a&gt; going over it in detail!&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%2Fdelq3c2gp2joqbe69yaj.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%2Fdelq3c2gp2joqbe69yaj.png" alt=" " width="512" height="239"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There’s also a &lt;a href="https://github.com/typicode/husky" rel="noopener noreferrer"&gt;husky&lt;/a&gt; precommit hook with &lt;a href="https://github.com/eslint/eslint" rel="noopener noreferrer"&gt;ESlint&lt;/a&gt; and &lt;a href="https://github.com/prettier/prettier" rel="noopener noreferrer"&gt;Prettier&lt;/a&gt; fixing and formatting changed code on every commit with &lt;a href="https://github.com/okonet/lint-staged" rel="noopener noreferrer"&gt;lint-staged&lt;/a&gt; which makes sure malformed code doesn't even make it to the Pull Request. Combined with &lt;a href="https://github.com/conventional-changelog/standard-version" rel="noopener noreferrer"&gt;standard-version&lt;/a&gt; and strict commit messages it makes releasing and changelog generation a breeze. For a contributor the workflow is as smooth as:&lt;/p&gt;

&lt;p&gt;fork → npm ci → npm start → code → commit → push&lt;/p&gt;

&lt;h1&gt;
  
  
  What’s new?
&lt;/h1&gt;

&lt;p&gt;When 2.0 went public it already was pretty big. But over the course of 2021 we added and improved a lot of things. With ideas and help from the community we significantly expanded customization and localization capabilities. This would be very helpful for our own international projects too. Taiga UI today has over 10 languages for built-in texts, RTL support for most of the components and various formats for money and dates. We also provide “ng add” schematic to get you started smoothly with Taiga UI. Let’s look at the most notable additions to our 150+ components base this year!&lt;/p&gt;

&lt;h2&gt;
  
  
  New components
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://taiga-ui.dev/components/sheet" rel="noopener noreferrer"&gt;Sheet&lt;/a&gt;&lt;/strong&gt; — mobile sliding dialog&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%2Fpmrmtd9tqoyi7j1qgtnp.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%2Fpmrmtd9tqoyi7j1qgtnp.png" alt=" " width="333" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://taiga-ui.dev/components/tree" rel="noopener noreferrer"&gt;Tree&lt;/a&gt;&lt;/strong&gt; — a flexible component to display tree-like data structures&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%2Fhmdc5346q5tefw14kiif.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%2Fhmdc5346q5tefw14kiif.png" alt=" " width="496" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://taiga-ui.dev/components/pdf-viewer" rel="noopener noreferrer"&gt;PdfViewer&lt;/a&gt;&lt;/strong&gt; — a custom dialog to preview PDFs in iframe&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%2Fvpnrss9fhyq5bjayazgi.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%2Fvpnrss9fhyq5bjayazgi.png" alt=" " width="512" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://taiga-ui.dev/components/carousel" rel="noopener noreferrer"&gt;Carousel&lt;/a&gt;&lt;/strong&gt; — a common UI pattern for slider of images&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%2Fw1rmy7sguc6tpi29uiwj.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%2Fw1rmy7sguc6tpi29uiwj.png" alt=" " width="512" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://taiga-ui.dev/components/arc-chart" rel="noopener noreferrer"&gt;ArcChart&lt;/a&gt;&lt;/strong&gt; — a new way to display numerical data&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%2Fl2xr0u9vmcpm9ic5j335.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%2Fl2xr0u9vmcpm9ic5j335.png" alt=" " width="512" height="207"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  New add-on packages
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://taiga-ui.dev/components/table" rel="noopener noreferrer"&gt;Table&lt;/a&gt;&lt;/strong&gt; — an interactive table component and related utilities&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%2Frlyh5dfe2wz4jqtd9rp5.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%2Frlyh5dfe2wz4jqtd9rp5.png" alt=" " width="512" height="258"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://taiga-ui.dev/components/editor-new" rel="noopener noreferrer"&gt;Editor&lt;/a&gt;&lt;/strong&gt; — a rich text editor based on &lt;a href="https://tiptap.dev/" rel="noopener noreferrer"&gt;tiptap 2&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%2Fk09jqb9rzbm2bbeldjt7.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%2Fk09jqb9rzbm2bbeldjt7.png" alt=" " width="512" height="243"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://taiga-ui.dev/components/preview" rel="noopener noreferrer"&gt;Preview&lt;/a&gt;&lt;/strong&gt; — a custom dialog to preview aribtrary content such as images and documents&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%2Fv0alvb5kmnig0f8nx893.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%2Fv0alvb5kmnig0f8nx893.png" alt=" " width="512" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We’ve also added a bunch of helpful utils, tokens, services and directives, such as Pan, Swipe, DropdownHover and more and a night theme for all components:&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%2Fkhkbdzh9ei1jy0h9jy06.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%2Fkhkbdzh9ei1jy0h9jy06.png" alt=" " width="512" height="314"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  What’s coming?
&lt;/h1&gt;

&lt;p&gt;It was a productive year. We’ve grown a lot in infratructure and content, we started to build community of users, most notably from cryptocurrency area as it seems from the screenshots that come with user questions 🙂 In the coming year we plan to exapand our localization and internationalization options so that developers from across the globe can enjoy Taiga UI and use it in whatever project they are working on. Here’s a brief overview of what we plan to focus on in 2022:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Improve and stabilize new Editor add-on package&lt;/li&gt;
&lt;li&gt;Make all native inputs accessible from the outside of Taiga UI controls so it’s easy to assign attributes and listen to native events&lt;/li&gt;
&lt;li&gt;Rework some of our internals to rely more on CSS and less on JavaScript which would reduce change detection cycles&lt;/li&gt;
&lt;li&gt;Utilize DI more for customization&lt;/li&gt;
&lt;li&gt;Drop dependency on global styles&lt;/li&gt;
&lt;li&gt;Create ComponentHarnesses for Taiga UI components so they can be easily tested&lt;/li&gt;
&lt;li&gt;Support different implementations of notifications, like we do now for dialogs&lt;/li&gt;
&lt;li&gt;Reduce tech debt such as old components waiting for refactor and outdated mask library&lt;/li&gt;
&lt;li&gt;Prepare for the next major release with Angular 13 and Ivy distribution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope you’re happy with Taiga UI and if you didn’t try it — please give it a shot! Feel free to open &lt;a href="https://github.com/TinkoffCreditSystems/taiga-ui/discussions" rel="noopener noreferrer"&gt;GitHub discussion&lt;/a&gt; or contact us over at &lt;a href="https://discord.gg/zrB2EdJjEy" rel="noopener noreferrer"&gt;Angular Discord&lt;/a&gt;. For Russian speaking users we have a &lt;a href="https://t.me/taiga_ui" rel="noopener noreferrer"&gt;Telegram chat&lt;/a&gt;. Our issues list is open for feature requests and bug reports. And it also always has &lt;a href="https://github.com/TinkoffCreditSystems/taiga-ui/issues?q=is%3Aissue+is%3Aopen+label%3A%22contributions+welcome%22" rel="noopener noreferrer"&gt;some things for you to try and solve&lt;/a&gt; if you want to contribute! Merry Christmas and an awesome New Year to you all! 🎄&lt;/p&gt;

</description>
      <category>angular</category>
      <category>opensource</category>
      <category>typescript</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Demystifying Taiga UI root component: portals pattern in Angular</title>
      <dc:creator>Alex Inkin</dc:creator>
      <pubDate>Tue, 26 Jan 2021 12:14:59 +0000</pubDate>
      <link>https://dev.to/taiga-ui/demystifying-taiga-ui-root-component-portals-pattern-in-angular-1fal</link>
      <guid>https://dev.to/taiga-ui/demystifying-taiga-ui-root-component-portals-pattern-in-angular-1fal</guid>
      <description>&lt;p&gt;Just before new year,&amp;nbsp;&lt;a href="https://twitter.com/marsibarsi" rel="noopener noreferrer"&gt;Roman&lt;/a&gt;, my colleague,&amp;nbsp;&lt;a href="https://indepth.dev/posts/1413/taiga-ui" rel="noopener noreferrer"&gt;announced&lt;/a&gt;&amp;nbsp;our new Angular UI kit library&amp;nbsp;&lt;a href="https://github.com/TinkoffCreditSystems/taiga-ui" rel="noopener noreferrer"&gt;Taiga UI&lt;/a&gt;. If you go through&amp;nbsp;&lt;a href="https://taiga-ui.dev/getting-started" rel="noopener noreferrer"&gt;Getting started&lt;/a&gt;&amp;nbsp;steps, you will see that you need to wrap your app with the&amp;nbsp;&lt;code&gt;tui-root&lt;/code&gt;&amp;nbsp;component. Let's see what it does and explore what portals are and how and why we use them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a portal?
&lt;/h2&gt;

&lt;p&gt;Imagine you have a select component. It has a drop-down block with suggestions. If we keep it at the same position in DOM as the hosting component, we will run into all sorts of trouble. Items pop through and containers can chop off content:&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%2Flh3.googleusercontent.com%2FpDxYWjQAQw9IlXhKaiR9Z8KgaHuFouln4FJbb2W1AipjnJLIgxBBcs9__-nF8iAVD9A1ksueyCHi-bosc7bNO2EaxX_7BQUpPbZs16vemgdPIizFYAgvpUcPGeE5QYuK9Yz8gwaZ" 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%2Flh3.googleusercontent.com%2FpDxYWjQAQw9IlXhKaiR9Z8KgaHuFouln4FJbb2W1AipjnJLIgxBBcs9__-nF8iAVD9A1ksueyCHi-bosc7bNO2EaxX_7BQUpPbZs16vemgdPIizFYAgvpUcPGeE5QYuK9Yz8gwaZ" width="774" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Verticality issues are often solved with&amp;nbsp;&lt;code&gt;z-index&lt;/code&gt;, effectively starting World War Z in your app. It's not uncommon to see values like 100, 10000, 10001. But even if you manage to get it right —&amp;nbsp;&lt;code&gt;overflow: hidden&lt;/code&gt;&amp;nbsp;would still get you there. So what can we do? Instead of having drop-down near its host we can show it in a dedicated container on top of everything. Then your application content can live in its own&amp;nbsp;&lt;a href="https://developer.mozilla.org/ru/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context" rel="noopener noreferrer"&gt;isolated context&lt;/a&gt;&amp;nbsp;eliminating&amp;nbsp;&lt;code&gt;z-index&lt;/code&gt;&amp;nbsp;problems. This container is exactly what a portal is. And it is what the Taiga UI root component sets up for you, among other things. Let's look at its template:&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-scroll-controls&amp;gt;&amp;lt;/tui-scroll-controls&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;tui-portal-host&amp;gt;&lt;/span&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;"content"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;ng-content&amp;gt;&amp;lt;/ng-content&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;tui-dialog-host&amp;gt;&amp;lt;/tui-dialog-host&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ng-content&lt;/span&gt; &lt;span class="na"&gt;select=&lt;/span&gt;&lt;span class="s"&gt;"tuiOverDialogs"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/ng-content&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;tui-notifications-host&amp;gt;&amp;lt;/tui-notifications-host&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ng-content&lt;/span&gt; &lt;span class="na"&gt;select=&lt;/span&gt;&lt;span class="s"&gt;"tuiOverNotifications"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/ng-content&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/tui-portal-host&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;ng-content&lt;/span&gt; &lt;span class="na"&gt;select=&lt;/span&gt;&lt;span class="s"&gt;"tuiOverPortals"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/ng-content&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;tui-hints-host&amp;gt;&amp;lt;/tui-hints-host&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;ng-content&lt;/span&gt; &lt;span class="na"&gt;select=&lt;/span&gt;&lt;span class="s"&gt;"tuiOverHints"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/ng-content&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Generic and dedicated portals
&lt;/h2&gt;

&lt;p&gt;Both&amp;nbsp;&lt;code&gt;tui-dialog-host&lt;/code&gt;&amp;nbsp;and&amp;nbsp;&lt;code&gt;tui-portal-host&lt;/code&gt;&amp;nbsp;are portals in their nature. But they work differently. Let's explore the second one first. Taiga UI uses it mostly to display drop-downs. But it's a generic container. It is controlled by a very simple service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="nf"&gt;@Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="n"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;export&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TuiPortalService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TuiPortalHostComponent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;add&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
    &lt;span class="n"&gt;componentFactory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ComponentFactory&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;
    &lt;span class="n"&gt;injector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Injector&lt;/span&gt;
  &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;ComponentRef&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&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="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addComponentChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;componentFactory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;injector&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;({&lt;/span&gt;&lt;span class="n"&gt;hostView&lt;/span&gt;&lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="n"&gt;ComponentRef&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;hostView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;addTemplate&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
    &lt;span class="n"&gt;templateRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TemplateRef&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="n"&gt;C&lt;/span&gt;
  &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;EmbeddedViewRef&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&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="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addTemplateChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;templateRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;removeTemplate&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;viewRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;EmbeddedViewRef&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;viewRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the component itself is rather straightforward. All it does is show templates and dynamic components on top of everything. No other logic is included (except a little&amp;nbsp;&lt;code&gt;position: fixed&lt;/code&gt;&amp;nbsp;helper for iOS). It means that positioning, closing and the rest is handled by portal items on their own. It's a good idea to have a generic portal for special cases. Like a fixed «Scroll to top» button displayed above content or anything else you, as a library user might need.&lt;/p&gt;

&lt;h2&gt;
  
  
  Drop-downs
&lt;/h2&gt;

&lt;p&gt;If we were to architect a drop-down — we would need to come up with a positioning solution. We have several options here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Position drop-down once and prevent scrolling while it's open. This is what material does by default.&lt;/li&gt;
&lt;li&gt; Position once and close if scrolling occurred. That's how native drop-downs behave.&lt;/li&gt;
&lt;li&gt; Follow host position when it changes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We went with the third option. It's not that trivial as it turned out. You cannot really get two positions in sync, even with&amp;nbsp;&lt;code&gt;requestAnimationFrame&lt;/code&gt;. Because once you query the host position — it triggers a layout recalculation. So by the time the next frame comes and drop-down is positioned — the host already changes location a little bit. This causes visible jumps, even on fast machines. We got around that by using absolute positioning, rather than fixed. Because the portal container wraps the entire page, position values stay the same during scroll. If the host is in a fixed container, though, it would still jump. But we can detect that when we open the drop-down and use fixed positioning for it as well.&lt;/p&gt;

&lt;p&gt;And then there's this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh4.googleusercontent.com%2F6B9coKjAaDvDtM0HzbHiEuP9RXWe17C488dJN94Pfhe7ToKQ_-kj9M9xZYKPvO4tY5NJ7h9gLaQRJfC0AcJ_7SRLHriOsNM_jsBbn6bPgBRQDM-V6v_WLjkSDSEXm8lWJ96Xh4Gi" 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%2Flh4.googleusercontent.com%2F6B9coKjAaDvDtM0HzbHiEuP9RXWe17C488dJN94Pfhe7ToKQ_-kj9M9xZYKPvO4tY5NJ7h9gLaQRJfC0AcJ_7SRLHriOsNM_jsBbn6bPgBRQDM-V6v_WLjkSDSEXm8lWJ96Xh4Gi" width="600" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If the host leaves the visible area — we need to close the drop-down. That's a job for Obscured service. It detects when the host is fully obscured by anything and closes drop-down in that case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dialogs
&lt;/h2&gt;

&lt;p&gt;For dedicated portals study we can take a look at dialogs. Toast notifications and hints are very similar but there are some interesting topics to discuss with modals.&lt;/p&gt;

&lt;p&gt;This is how dialog host looks like:&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;section&lt;/span&gt;
   &lt;span class="na"&gt;*ngFor=&lt;/span&gt;&lt;span class="s"&gt;"let item of dialogs$ | async"&lt;/span&gt;
   &lt;span class="na"&gt;polymorpheus-outlet&lt;/span&gt;
   &lt;span class="na"&gt;tuiFocusTrap&lt;/span&gt;
   &lt;span class="na"&gt;tuiOverscroll=&lt;/span&gt;&lt;span class="s"&gt;"all"&lt;/span&gt;
   &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"dialog"&lt;/span&gt;
   &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"dialog"&lt;/span&gt;
   &lt;span class="na"&gt;aria-modal=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;
   &lt;span class="na"&gt;[attr.aria-labelledby]=&lt;/span&gt;&lt;span class="s"&gt;"item.id"&lt;/span&gt;
   &lt;span class="na"&gt;[content]=&lt;/span&gt;&lt;span class="s"&gt;"item.component"&lt;/span&gt;
   &lt;span class="na"&gt;[context]=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;
   &lt;span class="err"&gt;[@&lt;/span&gt;&lt;span class="na"&gt;tuiParentAnimation&lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&amp;lt;/section&amp;gt;&lt;/span&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;"overlay"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of being a generic host it has an&amp;nbsp;&lt;code&gt;ngFor&lt;/code&gt;&amp;nbsp;loop over particular items. This allows us to bundle some logic in, like focus trap and page scroll blocking. There is also a clever use of dependency injection here, allowing dialogs to be design and data model agnostic. Host collects observables with dialogs through a dedicated multi token, merges these streams and shows the result. That way you can have multiple designs for dialogs in the same app. Taiga UI has two built-in designs — base and mobile. But you can easily add your own. Let's see how.&lt;/p&gt;

&lt;p&gt;Dialog service returns&amp;nbsp;&lt;code&gt;Observable&lt;/code&gt;. When you subscribe to it, modal popup is shown, when you terminate subscription it is closed. Dialog can also send back data through that stream. First, we design our dialog component. All that's important here, really, is that you can inject&amp;nbsp;&lt;code&gt;POLYMORPHEUS_CONTEXT&lt;/code&gt;&amp;nbsp;in constructor. It would contain an object with&amp;nbsp;&lt;code&gt;content&lt;/code&gt;&amp;nbsp;and&amp;nbsp;&lt;code&gt;observer&lt;/code&gt;&amp;nbsp;for a particular dialog instance. You can close dialog from within by calling&amp;nbsp;&lt;code&gt;complete&lt;/code&gt;&amp;nbsp;on&amp;nbsp;&lt;code&gt;observer&lt;/code&gt;&amp;nbsp;and you can send back data using&amp;nbsp;&lt;code&gt;next&lt;/code&gt;&amp;nbsp;method. Plus all the options you will provide to the service that we will create by extending an abstract class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;DIALOG&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;PolymorpheusComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MyDialogComponent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;DEFAULT_OPTIONS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MyDialogOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sc"&gt;'s'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nf"&gt;@Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="n"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;export&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyDialogService&lt;/span&gt; &lt;span class="n"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;AbstractTuiDialogService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MyDialogOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;component&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DIALOG&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;defaultOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DEFAULT_OPTIONS&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;In it we provide default config and a component to use and we're all set.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Dialogs, like everything in Taiga UI use&amp;nbsp;&lt;/em&gt;&lt;a href="https://github.com/TinkoffCreditSystems/ng-polymorpheus" rel="noopener noreferrer"&gt;&lt;em&gt;ng-polymorpheus&lt;/em&gt;&lt;/a&gt;&lt;em&gt;&amp;nbsp;for customizable content. You can read more about making interface free, flexible components with it in&amp;nbsp;&lt;/em&gt;&lt;a href="https://indepth.dev/posts/1314/agnostic-components-in-angular" rel="noopener noreferrer"&gt;&lt;em&gt;this article&lt;/em&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Focus trapping is handled by the&amp;nbsp;&lt;code&gt;tuiFocusTrap&lt;/code&gt;&amp;nbsp;directive. Since we have drop-downs later in DOM and we can have multiple dialogs open at the same time — we don't care if focus goes farther in the DOM. If it went somewhere prior to dialog though — we return focus back with a few helpers from&amp;nbsp;&lt;code&gt;@taiga-ui/cdk&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="nf"&gt;@HostListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;focusin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;silent&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;'$&lt;/span&gt;&lt;span class="k"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="nf"&gt;onFocusIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Node&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="nf"&gt;containsOrAfter&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="n"&gt;elementRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;focusable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getClosestKeyboardFocusable&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="n"&gt;elementRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;false&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="n"&gt;elementRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nativeElement&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="n"&gt;focusable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;focusable&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Blocking page scroll is dealt with by combination of a directive and some logic inside the root component. Root just hides scrollbars when a dialog is open, while Overscroll directive takes care of touch and wheel scroll. There's a&amp;nbsp;&lt;a href="https://developer.mozilla.org/ru/docs/Web/CSS/overscroll-behavior" rel="noopener noreferrer"&gt;CSS rule for overscroll behavior&lt;/a&gt;. However it's not sufficient. It doesn't help when dialog is small enough that it doesn't have its own scroll. That's why we have a directive with some additional logic stopping scroll if it will happen in some patent node.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: what else does tui-root do?
&lt;/h2&gt;

&lt;p&gt;As far as portals go — this covers most of it. Let's also take a quick look at what else is bundled with the root component. You saw in the template that it has&amp;nbsp;&lt;code&gt;tui-scroll-controls&lt;/code&gt;. These are custom scrollbars that control global scroll. You may have also noticed named content projections like&amp;nbsp;&lt;code&gt;&amp;lt;ng-content select="tuiOverDialogs"&amp;gt;&amp;lt;/ng-content&amp;gt;&lt;/code&gt;. With those you can slide some content in-between layers of Taiga UI if you need. For example, if you run another library for toasts or dialogs and want them properly placed vertically.&lt;/p&gt;

&lt;p&gt;It also registers several&amp;nbsp;&lt;a href="https://github.com/TinkoffCreditSystems/ng-event-plugins" rel="noopener noreferrer"&gt;event manager plugins&lt;/a&gt;&amp;nbsp;in the DI. You can read about them in a&amp;nbsp;&lt;a href="https://indepth.dev/posts/1153/supercharge-event-management-in-your-angular-application" rel="noopener noreferrer"&gt;dedicated article&lt;/a&gt;. It is important that&amp;nbsp;&lt;code&gt;TuiRootModule&lt;/code&gt;&amp;nbsp;goes after&amp;nbsp;&lt;code&gt;BrowserModule&lt;/code&gt;&amp;nbsp;so they are registered at the right order. But don't worry — if you get it wrong you will see an assertion message in the console.&lt;/p&gt;

&lt;p&gt;That wraps it up for portals and the root component. Taiga UI is open-source and you can check it out on&amp;nbsp;&lt;a href="https://github.com/TinkoffCreditSystems/taiga-ui" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&amp;nbsp;and npm. You can also browse the&amp;nbsp;&lt;a href="https://taiga-ui.dev/" rel="noopener noreferrer"&gt;demo portal&lt;/a&gt;&amp;nbsp;with documentation and play with it using this&amp;nbsp;&lt;a href="https://stackblitz.com/edit/taiga" rel="noopener noreferrer"&gt;StackBlitz starter&lt;/a&gt;. Stay tuned for more articles on interesting features we have!&lt;/p&gt;

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