<?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: Edu Calvo</title>
    <description>The latest articles on DEV Community by Edu Calvo (@educalvolpz).</description>
    <link>https://dev.to/educalvolpz</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F605472%2F80a27dcd-e7aa-4001-82e8-d05d3a03f2e3.png</url>
      <title>DEV Community: Edu Calvo</title>
      <link>https://dev.to/educalvolpz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/educalvolpz"/>
    <language>en</language>
    <item>
      <title>I've been building an animation library for years — here's SmoothUI</title>
      <dc:creator>Edu Calvo</dc:creator>
      <pubDate>Sat, 18 Apr 2026 15:12:02 +0000</pubDate>
      <link>https://dev.to/educalvolpz/ive-been-building-an-animation-library-for-years-heres-smoothui-2mi2</link>
      <guid>https://dev.to/educalvolpz/ive-been-building-an-animation-library-for-years-heres-smoothui-2mi2</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; — &lt;a href="https://smoothui.dev" rel="noopener noreferrer"&gt;SmoothUI&lt;/a&gt; is a free, open-source collection of 75+ animated React components. shadcn/ui compatible — install any component with &lt;code&gt;npx shadcn@latest add&lt;/code&gt;. Every component respects &lt;code&gt;prefers-reduced-motion&lt;/code&gt;. MIT licensed. GitHub → &lt;a href="https://github.com/educlopez/smoothui" rel="noopener noreferrer"&gt;educlopez/smoothui&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How it started
&lt;/h2&gt;

&lt;p&gt;A few years ago I wanted to get better at animations. So I started building small things — a card that tilts, a button that reacts to your mouse, a file upload that actually feels alive. No roadmap, no library plans. Just a sandbox to learn.&lt;/p&gt;

&lt;p&gt;It grew. At some point it stopped being a sandbox and started being a library.&lt;/p&gt;

&lt;p&gt;Then shadcn happened. Then a dozen shadcn-style libraries happened. And it clicked that the thing I'd been quietly building — components with real attention to motion — fit right into that copy-paste, own-your-code model.&lt;/p&gt;

&lt;p&gt;So I pivoted. SmoothUI today is &lt;strong&gt;two things at once&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A shadcn-compatible library you can install component-by-component&lt;/li&gt;
&lt;li&gt;A set of tutorials that teach you how each component works — so if you don't like mine, you can build your own&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That second part matters to me. Animation is one of those skills where reading the code isn't enough — you need to understand &lt;em&gt;why&lt;/em&gt; a spring has bounce &lt;code&gt;0.1&lt;/code&gt; instead of &lt;code&gt;0.3&lt;/code&gt;, or why &lt;code&gt;ease-out&lt;/code&gt; feels right for entrances but wrong for hovers. The library exists, but the knowledge does too.&lt;/p&gt;

&lt;h2&gt;
  
  
  5 components I'm proud of
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Magnetic Button
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fapyqbteu4v45kqhywuzk.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fapyqbteu4v45kqhywuzk.gif" alt="Magnetic Button demo — button springs toward cursor" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The button follows your cursor. Not literally — it springs toward it within a configurable radius, then relaxes back when you leave. It feels like the button is faintly gravitational.&lt;/p&gt;

&lt;p&gt;The trick is &lt;code&gt;useSpring&lt;/code&gt; motion values mapped to cursor position, clamped to a radius. Dampening on the spring makes it yield instead of lagging.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MagneticButton&lt;/span&gt; &lt;span class="na"&gt;strength&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  Click me
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;MagneticButton&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Install:&lt;/strong&gt; &lt;code&gt;npx shadcn@latest add @smoothui/magnetic-button&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Scrollable Card Stack
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmlp8pypnveidpw7m7o2r.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmlp8pypnveidpw7m7o2r.gif" alt="Scrollable Card Stack demo — cards layer with scale + blur on scroll" width="760" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A stack of cards with real depth. As you scroll, each card scales (~0.08 per layer), blurs, and fades into the one behind it — then snaps with a spring (stiffness 250, damping 20).&lt;/p&gt;

&lt;p&gt;The blur is the detail most libraries skip. Without it you get flat layering. With it, your eye buys the illusion.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ScrollableCardStack&lt;/span&gt; &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;cardHeight&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Siri Orb
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Febfuc8bclq6grh87lbcb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Febfuc8bclq6grh87lbcb.gif" alt="Siri Orb demo — animated conic gradients" width="720" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Looks like Apple Intelligence on a good day. Three pastel conic gradients rotating at different multipliers (×1, ×2, ×-3), stacked with a backdrop-blur dot pattern in &lt;code&gt;mix-blend-mode: overlay&lt;/code&gt;. No Motion library — pure CSS animating a &lt;code&gt;@property --angle&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The part I want you to notice: blur, contrast, mask radius, and dot size all scale with the component's &lt;code&gt;size&lt;/code&gt; prop. It looks intentional at 30px AND at 192px, which is the hard part most "fancy" components skip.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SiriOrb&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"192px"&lt;/span&gt; &lt;span class="na"&gt;animationDuration&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Expandable Cards
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fffrz7m52p2h5phwec428.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fffrz7m52p2h5phwec428.gif" alt="Expandable Cards demo — cards grow horizontally on click" width="720" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cards that grow horizontally on click — 200px → 500px — using Motion's &lt;code&gt;layoutId&lt;/code&gt; to handle the reflow and cubic-bezier &lt;code&gt;(0.23, 1, 0.32, 1)&lt;/code&gt; for the ease-out.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;ease-out-quint&lt;/code&gt; is the part to study. Regular &lt;code&gt;ease-out&lt;/code&gt; feels mechanical. This one snaps forward hard, then settles — which is what "premium" motion usually is underneath.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Gooey Popover
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgm7wj2il6ftd06jyeeps.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgm7wj2il6ftd06jyeeps.gif" alt="Gooey Popover demo — SVG goo filter morphs trigger into content" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The popover doesn't appear, it &lt;em&gt;merges&lt;/em&gt; out of the trigger. An SVG &lt;code&gt;feGaussianBlur&lt;/code&gt; goo filter softens the edges while the shape morphs from circle to rectangle. The math is boring. The effect is not.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;GooeyPopover&lt;/span&gt; &lt;span class="na"&gt;trigger&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Menu&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Content&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;GooeyPopover&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A note on accessibility
&lt;/h2&gt;

&lt;p&gt;All five of these (and every other animated component) call &lt;code&gt;useReducedMotion&lt;/code&gt; from &lt;code&gt;motion/react&lt;/code&gt;. If your OS has "reduce motion" on, the animations collapse to instant state changes. This is non-negotiable for me — motion libraries without this are broken.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tech stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;React 19&lt;/strong&gt; (uses ref-as-prop, no &lt;code&gt;forwardRef&lt;/code&gt; dance)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Motion&lt;/strong&gt; (née Framer Motion) 12&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tailwind CSS v4&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;shadcn/ui compatible&lt;/strong&gt; — install one component, keep the code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MIT licensed&lt;/strong&gt;, no dependencies you don't see&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install any component&lt;/span&gt;
npx shadcn@latest add @smoothui/magnetic-button

&lt;span class="c"&gt;# Or browse everything&lt;/span&gt;
https://smoothui.dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The component lands in your project. It's yours. Tweak the spring, swap the easing, rewrite the filter — the library is a starting point, not a black box.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;I'm working on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An npm-published &lt;code&gt;@smoothui/cli&lt;/code&gt; for batch installs&lt;/li&gt;
&lt;li&gt;An MCP server for AI-assisted component discovery inside Claude / Cursor&lt;/li&gt;
&lt;li&gt;More tutorials on the "why" behind specific animation choices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If any of this sounds useful, the project lives at &lt;strong&gt;&lt;a href="https://smoothui.dev" rel="noopener noreferrer"&gt;smoothui.dev&lt;/a&gt;&lt;/strong&gt; and on &lt;strong&gt;&lt;a href="https://github.com/educlopez/smoothui" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/strong&gt;. A ⭐ helps a lot; feedback helps more.&lt;/p&gt;

&lt;p&gt;Happy to answer questions about any component, animation decision, or the pivot from sandbox-to-library in the comments.&lt;/p&gt;

</description>
      <category>react</category>
      <category>animation</category>
      <category>tailwindcss</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
