<?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: Yury Uvarov</title>
    <description>The latest articles on DEV Community by Yury Uvarov (@uvarov-frontend).</description>
    <link>https://dev.to/uvarov-frontend</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%2F2496452%2F5b7516f2-247a-4444-823a-b1c00e660c8c.jpg</url>
      <title>DEV Community: Yury Uvarov</title>
      <link>https://dev.to/uvarov-frontend</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/uvarov-frontend"/>
    <language>en</language>
    <item>
      <title>Vanilla Disintegrate — Modern and Impressive Animations for the Web</title>
      <dc:creator>Yury Uvarov</dc:creator>
      <pubDate>Fri, 04 Sep 2026 17:13:36 +0000</pubDate>
      <link>https://dev.to/uvarov-frontend/vanilla-disintegrate-modern-particle-effects-for-the-web-3bhd</link>
      <guid>https://dev.to/uvarov-frontend/vanilla-disintegrate-modern-particle-effects-for-the-web-3bhd</guid>
      <description>&lt;h2&gt;
  
  
  The story behind it
&lt;/h2&gt;

&lt;p&gt;What do web animations usually look like? Fade in, fade out, a slight movement, some scaling.&lt;/p&gt;

&lt;p&gt;There is nothing wrong with that. Most interfaces don't need anything more complicated. Sometimes, the best solution is not to add animation at all. It keeps the UI fast, clear, and focused on what actually matters.&lt;/p&gt;

&lt;p&gt;But every once in a while, you want to make one small interaction memorable. Not a long intro or a heavyweight full-screen visual effect, but a short response to a user's action. Less than a second — and removing a card, restoring an element, or undoing an action suddenly feels completely different.&lt;/p&gt;

&lt;p&gt;I ran into exactly this situation while implementing yet another "remove from favorites" interaction. The usual flow is familiar: the user clicks a bookmark, the card disappears, and the surrounding elements smoothly shift into place. You can animate &lt;code&gt;opacity&lt;/code&gt;, you can add a little &lt;code&gt;transform&lt;/code&gt;, but these patterns are so familiar that we barely notice them anymore.&lt;/p&gt;

&lt;p&gt;I wanted something more interesting without making the interaction distracting. I had seen a similar animation when deleting messages in a messenger. Later, I learned that this kind of effect is often called the "Thanos snap."&lt;/p&gt;

&lt;p&gt;I assumed there had to be an existing solution. There were a few similar implementations, but none of them quite matched what I needed. I wasn't looking for a hardcoded implementation of a single effect. I wanted something that could work with regular DOM elements, remain framework-agnostic, and let me adapt the animation to the interface around it.&lt;/p&gt;

&lt;p&gt;So I built my own version for the project I was working on.&lt;/p&gt;

&lt;p&gt;When I created my first open-source project, I started asking myself a question whenever I solved something interesting at work: could this be useful to other developers too?&lt;/p&gt;

&lt;p&gt;Nobody pays me to turn these things into open-source libraries, and I still can't fully explain why I spend so much time doing it. I think I simply like the idea that code I wrote might be used by people in completely different parts of the world — from someone building their first project to an experienced developer — and that someone might notice and appreciate my work.&lt;/p&gt;

&lt;p&gt;And perhaps I just want to leave something useful behind in the digital world.&lt;/p&gt;

&lt;p&gt;That small work task eventually grew into another open-source library — &lt;a href="https://disintegrate.uvarov.tech" rel="noopener noreferrer"&gt;Vanilla Disintegrate&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It animates the removal and restoration of DOM elements using particle effects. It comes with ready-to-use presets, but you don't have to tweak configuration values blindly in your code. The website includes an interactive playground where you can adjust particle movement, timing, and sound, preview the result, and copy the generated configuration.&lt;/p&gt;

&lt;p&gt;And if the built-in particle renderer isn't enough, you can replace the rendering approach entirely.&lt;/p&gt;

&lt;p&gt;Below, I'll explain how it works, why "remove an element, but make it look cool" turns out to involve much more than a few CSS animations, and where the real limitations of this approach are.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your first effect in a few lines
&lt;/h2&gt;

&lt;p&gt;In the simplest case, you can initialize the library like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Disintegrator&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vanilla-disintegrate/snapdom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;effects&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Disintegrator&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;preset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dust&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;card&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.card&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#remove&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;card&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;effects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;card&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;After the click, the card disintegrates and the original element is removed from the DOM.&lt;/p&gt;

&lt;p&gt;There is no special markup, no mandatory CSS file, and no component tied to a particular framework.&lt;/p&gt;

&lt;p&gt;At first glance, it may seem like the library simply draws a nice animation over an element. But if it were that simple, a few CSS properties would have been enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why not CSS?
&lt;/h2&gt;

&lt;p&gt;CSS is excellent for many animation tasks. Showing a modal smoothly, highlighting a button, moving items in a list, creating a hover effect — CSS is usually exactly the right tool.&lt;/p&gt;

&lt;p&gt;But CSS animates a DOM element as a whole. It can't take an already rendered card — including its text, image, gradients, shadows, and other visual details — split it into hundreds of tiny fragments, and give every fragment its own trajectory.&lt;/p&gt;

&lt;p&gt;Technically, you could create hundreds of tiny &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; elements and animate them independently. But imagine what happens to performance when there are hundreds or thousands of those nodes. And even then, how do you reproduce the real text, images, shadows, and every other detail of the original element inside them?&lt;/p&gt;

&lt;p&gt;Instead, Vanilla Disintegrate first turns the element into an image and then uses that image as the source material for the particles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;regular DOM element
        ↓
Canvas snapshot
        ↓
particles rendered with WebGL2
        ↓
temporary layer above the page
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While the user is watching the particles, the original DOM element can already be gone from the list.&lt;/p&gt;

&lt;p&gt;Visually, the transition remains continuous: the card is still visible in the form of its captured image, but that image is now gradually turning into dust.&lt;/p&gt;

&lt;h2&gt;
  
  
  The hardest part: capturing the DOM
&lt;/h2&gt;

&lt;p&gt;Capturing a regular DOM element turned out to be much more interesting than I expected.&lt;/p&gt;

&lt;p&gt;The browser is obviously very good at rendering a web page. What it doesn't give us is a stable, universal API that says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Take this element and give me exactly the pixels the user is currently looking at.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So the visual representation of the element has to be reconstructed separately in a Canvas.&lt;/p&gt;

&lt;p&gt;The ready-to-use integration of Vanilla Disintegrate uses &lt;a href="https://github.com/zumerlab/snapdom" rel="noopener noreferrer"&gt;SnapDOM&lt;/a&gt;. It takes an element, transfers its content and styles into an intermediate representation, and produces a Canvas.&lt;/p&gt;

&lt;p&gt;That Canvas is enough to create the particle effect.&lt;/p&gt;

&lt;p&gt;But SnapDOM isn't part of the core library, nor is it the only possible option. Vanilla Disintegrate accepts a custom &lt;code&gt;capture&lt;/code&gt; adapter, so you can use another capture engine, provide a pre-rendered Canvas, or implement your own solution.&lt;/p&gt;

&lt;p&gt;This matters because different projects have different requirements for performance, visual fidelity, and the types of content they need to capture.&lt;/p&gt;

&lt;p&gt;And there is an important limitation worth stating explicitly: a DOM snapshot will not always match the original element pixel-for-pixel.&lt;/p&gt;

&lt;p&gt;Differences can be particularly noticeable with isolated characters, very thin lines, complex filters, or unusual page scaling. For cards, images, and complete UI blocks, those differences are usually difficult to notice, but claiming perfect reproduction in every browser would be misleading.&lt;/p&gt;

&lt;h2&gt;
  
  
  Turning an image into particles
&lt;/h2&gt;

&lt;p&gt;WebGL2 is a browser API for rendering graphics on the GPU. It's commonly associated with 3D scenes, visualizations, and games, but it never became the default way to animate regular user interfaces.&lt;/p&gt;

&lt;p&gt;And for good reason.&lt;/p&gt;

&lt;p&gt;If you're animating a button, a modal, or a list transition, CSS or the Web Animations API is usually a better choice. They work directly with the DOM and don't require you to manually manage textures, write shaders, or clean up GPU resources.&lt;/p&gt;

&lt;p&gt;This effect is different.&lt;/p&gt;

&lt;p&gt;We need to move a large number of small fragments of the same image independently. At that point, using the GPU makes much more sense than creating hundreds of DOM nodes.&lt;/p&gt;

&lt;p&gt;This is where the complexity of WebGL2 becomes justified.&lt;/p&gt;

&lt;p&gt;Once the Canvas snapshot is ready, Vanilla Disintegrate passes it to its built-in WebGL2 renderer. The renderer uploads the image as a texture and divides its non-transparent areas into small blocks. Each block becomes a particle.&lt;/p&gt;

&lt;p&gt;Every particle has its own initial position, release time, direction, velocity, rotation, and opacity. That makes it possible to create very different kinds of motion: directional bursts, chaotic scattering, slow dust, vapor-like movement, or swirling particles.&lt;/p&gt;

&lt;p&gt;The library currently includes four presets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dust&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Disintegrator&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;preset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dust&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Disintegrator&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;preset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;scatter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;vapor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Disintegrator&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;preset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vapor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wind&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Disintegrator&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;preset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wind&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But choosing a preset is only the beginning.&lt;/p&gt;

&lt;p&gt;Removal and restoration are two independent phases. For example, a card can burst to the left when removed and return from above with a slight swirl:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Disintegrator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;createParticleEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vanilla-disintegrate/snapdom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;effect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createParticleEffect&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;curve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;burst&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;release&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;left&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;700&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;restore&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;curve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;float&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;release&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;top&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;900&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;swirl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;effects&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Disintegrator&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;effect&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The point of this example isn't to demonstrate every available option. There are quite a few of them, and you don't need to memorize any of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Removing doesn't have to mean losing
&lt;/h2&gt;

&lt;p&gt;While building the library, I quickly realized that a removal animation alone wasn't enough.&lt;/p&gt;

&lt;p&gt;Real interfaces often have an &lt;strong&gt;Undo&lt;/strong&gt; action: a user removes a card from their favorites, changes their mind, and wants it back.&lt;/p&gt;

&lt;p&gt;Of course, you could create the element again. But sometimes it's much more convenient to retain the original DOM node and later insert it wherever the application needs it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;operation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;effects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;card&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;retain&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="c1"&gt;// The user clicked "Undo".&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;finished&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;retainedCard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;effects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;removalId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;retainedCard&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.favorites&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;retainedCard&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;effects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;restore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;retainedCard&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 card doesn't even have to return to its original position. It can be inserted into another list or into a layout that has changed since the removal.&lt;/p&gt;

&lt;p&gt;The application continues to manage the DOM normally. Vanilla Disintegrate simply handles the visual transition when the element appears wherever the application has placed it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not just WebGL2, and not just particles
&lt;/h2&gt;

&lt;p&gt;Particles are where the project started, but I didn't want to build a library capable of exactly one trick.&lt;/p&gt;

&lt;p&gt;Every effect in Vanilla Disintegrate consists of two independent phases: &lt;strong&gt;remove&lt;/strong&gt; and &lt;strong&gt;restore&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Each phase can use the Web Animations API, CSS, Canvas 2D, SVG, another WebGL library, or your own renderer. And if a particular effect doesn't need a Canvas snapshot at all, capture can be disabled for that phase.&lt;/p&gt;

&lt;p&gt;This means Vanilla Disintegrate doesn't have to be about the "Thanos snap."&lt;/p&gt;

&lt;p&gt;One part of an interface can use particles, another can use a subtle fade, and a third can use a completely custom animation. The DOM lifecycle, operation cancellation, audio, and cleanup of temporary resources remain shared across all of them.&lt;/p&gt;

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

&lt;p&gt;That's an obvious question.&lt;/p&gt;

&lt;p&gt;A particle effect is considerably more complex than changing &lt;code&gt;opacity&lt;/code&gt;, so naturally it requires more resources.&lt;/p&gt;

&lt;p&gt;There are two separate workloads involved. First, the library needs to capture the element. That's a short CPU-bound operation. After that, the particles are rendered on the GPU using WebGL2.&lt;/p&gt;

&lt;p&gt;A page can contain any number of cards that may use the effect, but that doesn't mean all of them are being animated simultaneously.&lt;/p&gt;

&lt;p&gt;Typically, an animation starts in response to a specific user action. One operation runs, its temporary resources are released when it finishes, and another operation may start later.&lt;/p&gt;

&lt;p&gt;In that scenario, simply having many elements on the page doesn't create significant additional load.&lt;/p&gt;

&lt;p&gt;Things change if you try to disintegrate several huge full-screen elements at once, especially on a high-DPI device.&lt;/p&gt;

&lt;p&gt;That's why Vanilla Disintegrate has an &lt;code&gt;auto&lt;/code&gt; quality mode. By default, it limits the working resolution so that a single effect can't unexpectedly consume an excessive amount of memory.&lt;/p&gt;

&lt;p&gt;For smaller elements where maximum detail matters, you can use &lt;code&gt;exact&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createParticleEffect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vanilla-disintegrate/snapdom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;effect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createParticleEffect&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;renderQuality&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;exact&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;restore&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;renderQuality&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;exact&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="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;But &lt;code&gt;exact&lt;/code&gt; isn't a magic switch.&lt;/p&gt;

&lt;p&gt;It preserves the full resolution of the snapshot that has already been captured. It doesn't make the DOM capture itself more accurate.&lt;/p&gt;

&lt;p&gt;If the captured representation differs slightly from the original DOM element, increasing its resolution won't fix that difference.&lt;/p&gt;

&lt;p&gt;Another important consideration is WebGL contexts.&lt;/p&gt;

&lt;p&gt;Browsers aren't designed for pages to create an unlimited number of new WebGL contexts for individual cards. So once an effect is finished, temporary textures are deleted, while contexts are reused through a small shared pool.&lt;/p&gt;

&lt;h2&gt;
  
  
  What if WebGL2 isn't available?
&lt;/h2&gt;

&lt;p&gt;WebGL2 is only required for the built-in particle effects.&lt;/p&gt;

&lt;p&gt;If the browser or device can't create a WebGL2 context, the user's action should still work. The element will still be removed or restored, while the visual phase completes with a &lt;code&gt;skipped&lt;/code&gt; status.&lt;/p&gt;

&lt;p&gt;That doesn't mean the user necessarily has to see an abrupt disappearance.&lt;/p&gt;

&lt;p&gt;Any effect that requires WebGL2 can have a fallback animation implemented with CSS, the Web Animations API, Canvas 2D, SVG, or a custom renderer. The fallback runs if WebGL2 isn't available or if the built-in particle renderer fails to start.&lt;/p&gt;

&lt;p&gt;So a modern device can get the full particle effect, while other environments fall back to a simple, graceful transition.&lt;/p&gt;

&lt;p&gt;Custom effects implemented with CSS, Canvas 2D, or the Web Animations API don't depend on WebGL2 at all.&lt;/p&gt;

&lt;p&gt;Vanilla Disintegrate also respects &lt;code&gt;prefers-reduced-motion&lt;/code&gt;, so unnecessary animation is skipped when the user has enabled reduced motion at the operating-system level.&lt;/p&gt;

&lt;p&gt;Vanilla Disintegrate targets modern browsers. I've documented the complete browser support matrix, capture limitations, and Safari-specific behavior in the &lt;a href="https://disintegrate.uvarov.tech/docs/learn/limitations/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; rather than turning this article into a compatibility reference.&lt;/p&gt;

&lt;h2&gt;
  
  
  When does an effect like this actually make sense?
&lt;/h2&gt;

&lt;p&gt;I don't think every close button in an interface should suddenly produce a cloud of particles.&lt;/p&gt;

&lt;p&gt;Animation for its own sake gets old quickly and eventually becomes distracting.&lt;/p&gt;

&lt;p&gt;But there are interactions where a stronger visual response can work well: removing an important card, clearing a list, undoing an action, completing a task, or moving something to the trash.&lt;/p&gt;

&lt;p&gt;In these situations, a short visual response can make the result of an action easier to notice — and simply make the interface feel a little more alive.&lt;/p&gt;

&lt;p&gt;That's what I built Vanilla Disintegrate for.&lt;/p&gt;

&lt;p&gt;It isn't intended to replace regular CSS animations. It's for those moments when you want one distinctive but controlled effect, without tying your UI to a framework or building an entire WebGL engine from scratch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;Vanilla Disintegrate is released under the MIT license and is free to use in any project.&lt;/p&gt;

&lt;p&gt;I'd be glad to see it become useful to other developers — whether that means a star on GitHub, an issue describing a real-world use case, an idea for a new effect, or simply a link to something you've built with it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Documentation and playground:&lt;/strong&gt; &lt;a href="https://disintegrate.uvarov.tech" rel="noopener noreferrer"&gt;disintegrate.uvarov.tech&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/uvarov-frontend/vanilla-disintegrate" rel="noopener noreferrer"&gt;uvarov-frontend/vanilla-disintegrate&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;See the effect in a real project:&lt;/strong&gt; &lt;a href="https://vanilla-calendar.pro/docs/learn/" rel="noopener noreferrer"&gt;Vanilla Calendar Pro&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>webgl</category>
      <category>opensource</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How I Created Vanilla Calendar Pro — A Lightweight and Flexible JavaScript Calendar with TypeScript</title>
      <dc:creator>Yury Uvarov</dc:creator>
      <pubDate>Thu, 28 Nov 2024 14:14:26 +0000</pubDate>
      <link>https://dev.to/uvarov-frontend/how-i-created-vanilla-calendar-pro-a-lightweight-and-flexible-javascript-calendar-with-typescript-21if</link>
      <guid>https://dev.to/uvarov-frontend/how-i-created-vanilla-calendar-pro-a-lightweight-and-flexible-javascript-calendar-with-typescript-21if</guid>
      <description>&lt;p&gt;Hello, Dev Community!&lt;/p&gt;

&lt;p&gt;Honestly, I hesitated for a long time before writing this article. I understand that there might be comments saying, &lt;em&gt;"Just another unnecessary datepicker."&lt;/em&gt; But today, with Vanilla Calendar Pro earning over &lt;strong&gt;480 stars on GitHub&lt;/strong&gt; and receiving &lt;strong&gt;17,000 downloads per month&lt;/strong&gt;, I decided to share my story.&lt;/p&gt;

&lt;p&gt;Let me clarify upfront: I'm not claiming to be the best calendar or datepicker developer. This article is about my first experience with open source, how I created a project from scratch, and how it became useful to other developers.&lt;/p&gt;




&lt;h2&gt;
  
  
  How It All Started
&lt;/h2&gt;

&lt;p&gt;In the summer of 2022, I needed to implement an event calendar for a project. The requirements were simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It had to work seamlessly in a modal window.&lt;/li&gt;
&lt;li&gt;It needed to support custom styling to match the design.&lt;/li&gt;
&lt;li&gt;Clicking a date would redirect the user to an event page.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At first glance, it seemed like a straightforward task. But I couldn’t find a suitable solution. Most existing calendars either lacked the necessary features or were too bulky, with styles that were difficult to adapt to the project’s design.&lt;/p&gt;

&lt;p&gt;Sure, I could have asked the designer to simplify the layout or just accepted a minor formatting issue. But honestly, I was intrigued: &lt;em&gt;What if I built my own calendar?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That’s how the first version of &lt;strong&gt;Vanilla Calendar Pro&lt;/strong&gt; was born. The term &lt;em&gt;"Vanilla"&lt;/em&gt; refers to native JavaScript, free of external dependencies. To make it easier to integrate into other projects, I wrote a README with API documentation and examples and published it on NPM.&lt;/p&gt;




&lt;h2&gt;
  
  
  The First User — And the First Star
&lt;/h2&gt;

&lt;p&gt;Not long after, I received a notification about the first issue in the repository. It came from a developer in Norway, who asked an unexpected question:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Do you have any plans to add week numbers to the calendar? Here in Norway, using week numbers is very common, and it would be a helpful feature."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This moment made me realize that the calendar could be useful not just for me, but for others as well. But then I asked myself: &lt;em&gt;What do I gain from all this effort? Why should I spend my free time so some guy in Norway can use my calendar?&lt;/em&gt; The answer was: &lt;em&gt;Nothing tangible!&lt;/em&gt;  &lt;/p&gt;

&lt;p&gt;However, the thought that I had created something genuinely helpful—that someone could download my calendar, save time on development, and focus on more important tasks—was incredibly inspiring. Even if it didn’t bring me any material rewards, I felt a deep sense of satisfaction knowing that my project was helping others.&lt;/p&gt;

&lt;p&gt;So, I began actively improving the calendar, transforming it into a more universal and flexible solution. I also designed the interface myself, aiming to make it not only functional but also user-friendly.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Sets Vanilla Calendar Pro Apart?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Flexible layouts:&lt;/strong&gt; Customize the HTML structure of the calendar completely, adding custom buttons or elements as needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full style control:&lt;/strong&gt; Easily override any CSS classes, enabling seamless integration with frameworks like Tailwind CSS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accessibility (a11y):&lt;/strong&gt; All elements are equipped with &lt;code&gt;aria-labels&lt;/code&gt; and support keyboard navigation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interactive popups:&lt;/strong&gt; Highlight specific days and display additional information directly in the calendar.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Week numbers:&lt;/strong&gt; Supports week-based date formats, popular in many countries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time selection:&lt;/strong&gt; Offers both 12-hour and 24-hour formats, with sliders or manual input.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Themes:&lt;/strong&gt; Automatically switches between light and dark themes and supports custom themes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Localization and internationalization:&lt;/strong&gt; Easily adjust the first day of the week, weekends, and holidays for any locale.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Framework compatibility:&lt;/strong&gt; Works with React, Vue, Angular, and any other frameworks thanks to its native JavaScript foundation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And that’s not all! I didn’t even mention the utilities, methods, actions, and extensive configuration options that allow you to customize the calendar to fit virtually any requirements.&lt;/p&gt;




&lt;h2&gt;
  
  
  Numbers and Documentation
&lt;/h2&gt;

&lt;p&gt;Vanilla Calendar Pro is entirely written in TypeScript, with no external dependencies. The minified file is only &lt;strong&gt;~49.33 KB&lt;/strong&gt; (or &lt;strong&gt;13.52 KB gzip&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;I’ve also prepared detailed documentation, which includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An &lt;strong&gt;API reference&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;An &lt;strong&gt;Learn page&lt;/strong&gt; with interactive sandboxes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📘 &lt;a href="https://vanilla-calendar.pro" rel="noopener noreferrer"&gt;Project Website&lt;/a&gt;&lt;br&gt;&lt;br&gt;
🔗 &lt;a href="https://github.com/uvarov-frontend/vanilla-calendar-pro" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt;&lt;/p&gt;




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

&lt;p&gt;No, this is not a revolutionary product. It’s just another calendar/datepicker. But for me, it became a significant milestone in my journey with open source. I’m glad I created something useful that could help other developers.&lt;/p&gt;

&lt;p&gt;I hope Vanilla Calendar Pro proves helpful to you as well. Thank you for reading!&lt;/p&gt;

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