<?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>Forem: Alexander</title>
    <description>The latest articles on Forem by Alexander (@zizigy).</description>
    <link>https://forem.com/zizigy</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%2F3133527%2Ff57eb48c-7d27-4ad4-90a2-63781dbc11b0.jpg</url>
      <title>Forem: Alexander</title>
      <link>https://forem.com/zizigy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/zizigy"/>
    <language>en</language>
    <item>
      <title>The World's First Web Components Library in the Style of shadcn — With Automatic Code Injection</title>
      <dc:creator>Alexander</dc:creator>
      <pubDate>Tue, 02 Dec 2025 10:11:08 +0000</pubDate>
      <link>https://forem.com/zizigy/the-worlds-first-web-components-library-in-the-style-of-shadcn-with-automatic-code-injection-33on</link>
      <guid>https://forem.com/zizigy/the-worlds-first-web-components-library-in-the-style-of-shadcn-with-automatic-code-injection-33on</guid>
      <description>&lt;p&gt;Okay, that headline sounds pretty cocky, I know. But as far as I've been able to Google — this really is the first attempt at something like this. If I'm wrong — drop a comment, I'd love to check out alternatives. Meanwhile, let me tell you what this beast is and why it even exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Backstory, or How I Got Here
&lt;/h2&gt;

&lt;p&gt;It all started with microfrontends. You know, when you have one project, but inside it lives Vue, React, and maybe some legacy jQuery that nobody wants to touch because "it works, don't touch it."&lt;/p&gt;

&lt;p&gt;So you're sitting there, a designer brings you a mockup of a new button. A beautiful button, with a gradient, with hover effects, everything just right. And you realize that now you're going to have to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write this button for Vue&lt;/li&gt;
&lt;li&gt;Write the same button for React&lt;/li&gt;
&lt;li&gt;Write it one more time for that legacy code&lt;/li&gt;
&lt;li&gt;Pray that they look the same&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And this happens every single time. Every. Single. Time.&lt;/p&gt;

&lt;p&gt;At some point I thought: "What if I write the component once and use it everywhere?" Revolutionary thought, right? Actually no, Web Components have been around for a while. But somehow nobody made a decent DX for them, like shadcn/ui has.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is shadcn and Why It Matters
&lt;/h2&gt;

&lt;p&gt;For those who don't know: shadcn/ui isn't really a library in the traditional sense. It's more like a collection of components that you copy into your project. Not install as a dependency, but literally copy.&lt;/p&gt;

&lt;p&gt;Why this is awesome:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Full control over the code&lt;/li&gt;
&lt;li&gt;No version conflicts&lt;/li&gt;
&lt;li&gt;Customize however you want&lt;/li&gt;
&lt;li&gt;Delete what you don't need, add what you do&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The problem is that shadcn is written for React. And I needed something that works everywhere. Literally everywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  Meet CapsuleUI
&lt;/h2&gt;

&lt;p&gt;So, I went ahead and made the same thing, but for Web Components. It's called CapsuleUI, and it works roughly like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Initialize the project&lt;/span&gt;
npx @zizigy/capsule init

&lt;span class="c"&gt;# Add a component&lt;/span&gt;
npx @zizigy/capsule add Button
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. After that, you get a &lt;code&gt;@capsule&lt;/code&gt; folder in your project, and inside — a ready-to-use button component. Not a link to node_modules, not some magic — just files with code that you can open, read, and modify.&lt;/p&gt;

&lt;h2&gt;
  
  
  What It Looks Like in Use
&lt;/h2&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;capsule-button&lt;/span&gt; &lt;span class="na"&gt;variant=&lt;/span&gt;&lt;span class="s"&gt;"primary"&lt;/span&gt; &lt;span class="na"&gt;size=&lt;/span&gt;&lt;span class="s"&gt;"lg"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Click me
&lt;span class="nt"&gt;&amp;lt;/capsule-button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yeah, that simple. This works in React:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;capsule&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;button&lt;/span&gt; &lt;span class="na"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"primary"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        Hello from React
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;capsule&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;button&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;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works in Vue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;capsule-button&lt;/span&gt; &lt;span class="na"&gt;variant=&lt;/span&gt;&lt;span class="s"&gt;"primary"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Hello from Vue
  &lt;span class="nt"&gt;&amp;lt;/capsule-button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works in vanilla HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"@capsule/global.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"@capsule/index.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;capsule-button&lt;/span&gt; &lt;span class="na"&gt;variant=&lt;/span&gt;&lt;span class="s"&gt;"primary"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Hello from 2005
  &lt;span class="nt"&gt;&amp;lt;/capsule-button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the coolest part — it's the same component. Not three different implementations, but one. The only one. Unique.&lt;/p&gt;

&lt;h2&gt;
  
  
  But Wait, Web Components...
&lt;/h2&gt;

&lt;p&gt;Yeah, I know what you're going to say. "Web Components are complicated", "Shadow DOM is a pain", "How do you style them?", "What about SSR?".&lt;/p&gt;

&lt;p&gt;Let's go through this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Complexity
&lt;/h3&gt;

&lt;p&gt;Components are written with Lit. If you don't know — it's a lightweight library from Google for creating Web Components. It does all the dirty work for you: reactivity, templating, lifecycle. The code ends up clean and understandable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Shadow DOM
&lt;/h3&gt;

&lt;p&gt;Yeah, we use it. But it's not a problem, it's a feature. Component styles are isolated — they won't break your site, and your site won't break them. For customization there's CSS Custom Properties and &lt;code&gt;::part()&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Styling
&lt;/h3&gt;

&lt;p&gt;All components use CSS variables. Want to change the button color? Here you go:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--capsule-color-primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#8b5cf6&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;Want to completely rewrite the styles? The files are in your project, open and edit them.&lt;/p&gt;

&lt;h3&gt;
  
  
  SSR — Here's Where It Gets Interesting
&lt;/h3&gt;

&lt;p&gt;Okay, there's a nuance here. Web Components work with SSR. The server will render the component tag itself — it'll be in the HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;capsule-button&lt;/span&gt; &lt;span class="na"&gt;variant=&lt;/span&gt;&lt;span class="s"&gt;"primary"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Click me&lt;span class="nt"&gt;&amp;lt;/capsule-button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But if the component itself generates HTML inside Shadow DOM (for example, a complex structure with nested elements), that HTML won't be in SSR. The component tag will be there, the Shadow DOM content — not. This is normal behavior for Web Components, and usually it's not a problem, because the content still renders on the client.&lt;/p&gt;

&lt;p&gt;For most cases this works great. If you absolutely need full SSR with Shadow DOM content — there's Declarative Shadow DOM, but that's a whole other story.&lt;/p&gt;

&lt;h2&gt;
  
  
  The VSCode Feature I'm Proud Of
&lt;/h2&gt;

&lt;p&gt;You know what always pissed me off about Web Components? No autocomplete. You write &lt;code&gt;&amp;lt;my-component&lt;/code&gt; and your IDE goes: "I have no idea what this is, good luck bro."&lt;/p&gt;

&lt;p&gt;In CapsuleUI this works. When you add a component, the CLI automatically updates your VSCode settings. And your IDE starts understanding custom elements!&lt;/p&gt;

&lt;p&gt;You write &lt;code&gt;&amp;lt;capsule-button&lt;/code&gt; — you get autocomplete for &lt;code&gt;variant&lt;/code&gt;, &lt;code&gt;size&lt;/code&gt;, &lt;code&gt;disabled&lt;/code&gt;. Hover over it — see the documentation. Like it's a built-in HTML tag, only better.&lt;/p&gt;

&lt;p&gt;This works through &lt;code&gt;html.customData&lt;/code&gt; in VSCode settings. The CLI generates JSON with descriptions of all components and their attributes, and the IDE picks it up. Magic? No, just proper DX.&lt;/p&gt;

&lt;h2&gt;
  
  
  VSCode Data Generator — For the Advanced
&lt;/h2&gt;

&lt;p&gt;But what if you updated a component, added new attributes, and want to update autocomplete? Or you're working with your own components folder?&lt;/p&gt;

&lt;p&gt;For this there's the &lt;code&gt;generate&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Generates vscode.data.json for a specific folder&lt;/span&gt;
npx @zizigy/capsule generate &lt;span class="nt"&gt;--dir&lt;/span&gt; ./my-components

&lt;span class="c"&gt;# Or for a specific component folder&lt;/span&gt;
npx @zizigy/capsule generate &lt;span class="nt"&gt;--dir&lt;/span&gt; @capsule/components/capsule-button

&lt;span class="c"&gt;# You can specify the output directory&lt;/span&gt;
npx @zizigy/capsule generate &lt;span class="nt"&gt;--dir&lt;/span&gt; ./src/components &lt;span class="nt"&gt;--out&lt;/span&gt; ./vscode-config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command parses JavaScript and CSS files of components, extracts all attributes, their types and possible values, and generates &lt;code&gt;vscode.data.json&lt;/code&gt; for autocomplete. Clever? I think so.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Under the Hood
&lt;/h2&gt;

&lt;p&gt;Okay, let's get a bit technical for those who are interested.&lt;/p&gt;

&lt;h3&gt;
  
  
  Project Structure After Initialization
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@capsule/
├── components/
│   └── capsule-button/
│       ├── button.js
│       ├── button.style.css
│       └── register.js
├── global.css
└── index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything is logical and clear. Want to find button styles? Open &lt;code&gt;button.style.css&lt;/code&gt;. Want to change the logic? Open &lt;code&gt;button.js&lt;/code&gt;. No magic, no hidden files in node_modules.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reactivity
&lt;/h3&gt;

&lt;p&gt;Components are fully reactive thanks to Lit. Change an attribute — the component redraws:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;button&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="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;capsule-button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;variant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;secondary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Updates automatically&lt;/span&gt;
&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt; &lt;span class="o"&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;// This too&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works with both JavaScript and frameworks. Vue automatically binds attributes, React does too (with some nuances, but solvable).&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom Prefixes
&lt;/h3&gt;

&lt;p&gt;Don't like &lt;code&gt;capsule-&lt;/code&gt;? You can use your own:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @zizigy/capsule add Button &lt;span class="nt"&gt;--prefix&lt;/span&gt; ui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you'll get &lt;code&gt;&amp;lt;ui-button&amp;gt;&lt;/code&gt; instead of &lt;code&gt;&amp;lt;capsule-button&amp;gt;&lt;/code&gt;. Handy if you already have your own namespace.&lt;/p&gt;

&lt;h2&gt;
  
  
  Modules — A Different Story
&lt;/h2&gt;

&lt;p&gt;Besides components there are also modules. Like, additional functionality that isn't a component, but often needed.&lt;/p&gt;

&lt;p&gt;For example, a form validation module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @zizigy/capsule module add form
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that you get a validator with a bunch of ready-made rules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&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;CapsuleValidator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CapsuleRules&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;@capsule/modules/form&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;validator&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;CapsuleValidator&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;CapsuleRules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;required&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;CapsuleRules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;email&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;CapsuleRules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;required&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;CapsuleRules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;minLength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// result.isValid === false&lt;/span&gt;
&lt;span class="c1"&gt;// result.errors.password === ['Minimum length: 8 characters']&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again — it's your code. Want to add your own rule? Add it. Want to remove unnecessary ones? Remove them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Project's Philosophy
&lt;/h2&gt;

&lt;p&gt;CapsuleUI isn't a ready-made design system. It's more like a constructor for building your own.&lt;/p&gt;

&lt;p&gt;Components are intentionally minimalistic in styling. Basic states exist, but they're easily overridden. Because I have no idea what your design is. Maybe you have Material Design, maybe Tailwind-style, maybe something completely unique.&lt;/p&gt;

&lt;p&gt;The idea is to give you a working foundation that you'll customize for yourself. Not force you to "fight" with someone else's styles, trying to override them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real Use Cases
&lt;/h2&gt;

&lt;p&gt;Let me give you a few examples where this actually comes in handy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Microfrontends
&lt;/h3&gt;

&lt;p&gt;You have a project where part is Vue 3, part is React 18, and somewhere there's legacy jQuery. And you need all buttons to look the same. With CapsuleUI this is solved by adding one component — it works everywhere.&lt;/p&gt;

&lt;h3&gt;
  
  
  Unified Design System
&lt;/h3&gt;

&lt;p&gt;You work at a big company, and you have a bunch of different projects on different technologies. But everyone should use a unified design. Instead of maintaining separate libraries for each framework, you can make one set of Web Components.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quick Prototypes
&lt;/h3&gt;

&lt;p&gt;Need to quickly throw together a prototype, but don't want to drag in a whole framework? Web Components work in vanilla HTML. Add components — get working markup.&lt;/p&gt;

&lt;h3&gt;
  
  
  CMS Integration
&lt;/h3&gt;

&lt;p&gt;Many CMSs (WordPress, Drupal, etc.) allow using custom HTML. But they don't understand React or Vue. But Web Components? Sure thing. Your content editor can use your components.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who Is This For?
&lt;/h2&gt;

&lt;p&gt;Honestly, CapsuleUI is a niche tool. It's not for every project.&lt;/p&gt;

&lt;p&gt;It's perfect if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You have microfrontends with different frameworks&lt;/li&gt;
&lt;li&gt;You want to create your own design system on Web Components&lt;/li&gt;
&lt;li&gt;You need components that work everywhere&lt;/li&gt;
&lt;li&gt;You want full control over the code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It probably isn't for you if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You have a monolith on one framework (easier to take shadcn/ui for React or an analogue for Vue)&lt;/li&gt;
&lt;li&gt;You need a ready-made design system out of the box (take Vuetify, MUI, Ant Design)&lt;/li&gt;
&lt;li&gt;You don't want to bother with customization&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;Right now the library has a basic set of components that covers the main needs. But this is just the beginning.&lt;/p&gt;

&lt;p&gt;Planned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More components (modals, dropdowns, date pickers)&lt;/li&gt;
&lt;li&gt;Improved documentation&lt;/li&gt;
&lt;li&gt;Integration examples with different frameworks&lt;/li&gt;
&lt;li&gt;Possibly, CLI for generating your own components&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have ideas or suggestions — write in issues on GitHub. I really do read and respond.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Try It
&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;# Create a new project (or use an existing one)&lt;/span&gt;
&lt;span class="nb"&gt;mkdir &lt;/span&gt;my-project &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;my-project

&lt;span class="c"&gt;# Initialize&lt;/span&gt;
npx @zizigy/capsule init

&lt;span class="c"&gt;# Add components&lt;/span&gt;
npx @zizigy/capsule add Button
npx @zizigy/capsule add Alert
npx @zizigy/capsule add Tabs

&lt;span class="c"&gt;# See the list of available components&lt;/span&gt;
npx @zizigy/capsule list

&lt;span class="c"&gt;# If you updated a component, regenerate VSCode data&lt;/span&gt;
npx @zizigy/capsule generate &lt;span class="nt"&gt;--dir&lt;/span&gt; @capsule/components/capsule-button
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/ZiZIGY/CapsuleUI" rel="noopener noreferrer"&gt;github.com/ZiZIGY/CapsuleUI&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;npm:&lt;/strong&gt; &lt;a href="https://www.npmjs.com/package/@zizigy/capsule" rel="noopener noreferrer"&gt;@zizigy/capsule&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Documentation:&lt;/strong&gt; &lt;a href="https://zizigy.github.io/CapsuleUI/" rel="noopener noreferrer"&gt;capsuleui&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Instead of a Conclusion
&lt;/h2&gt;

&lt;p&gt;You know, when I started this project, I just wanted to solve a specific problem at work. And in the end I got something I'm really proud of.&lt;/p&gt;

&lt;p&gt;Web Components have long been "the technology of the future that never comes." But now they work in all browsers, there are great tools like Lit, and, I think, it's time to give them proper developer experience.&lt;/p&gt;

&lt;p&gt;CapsuleUI is my attempt to do this. Maybe not perfect. Maybe with bugs (if you find any — write, I'll fix them). But it works, and it solves a real problem.&lt;/p&gt;

&lt;p&gt;I'll be happy about feedback, stars on GitHub, and, of course, contributors. Because one person can't do everything alone, and a good open-source project is always a team effort.&lt;/p&gt;

&lt;p&gt;Thanks for reading to the end. You're awesome! ✨&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
      <category>webcomponents</category>
    </item>
    <item>
      <title>Introducing Vue DnD Kit: A New Library for Creating Drag and Drop Interfaces in Vue 3</title>
      <dc:creator>Alexander</dc:creator>
      <pubDate>Wed, 07 May 2025 11:48:51 +0000</pubDate>
      <link>https://forem.com/zizigy/introducing-vue-dnd-kit-a-new-library-for-creating-drag-and-drop-interfaces-in-vue-3-3i8c</link>
      <guid>https://forem.com/zizigy/introducing-vue-dnd-kit-a-new-library-for-creating-drag-and-drop-interfaces-in-vue-3-3i8c</guid>
      <description>&lt;h2&gt;
  
  
  Introducing Vue DnD Kit: A New Library for Creating Drag and Drop Interfaces in Vue 3 🚀
&lt;/h2&gt;

&lt;p&gt;Hello, DEV Community! Today I want to introduce my new project — &lt;a href="https://zizigy.github.io/vue-dnd-kit/" rel="noopener noreferrer"&gt;&lt;strong&gt;Vue DnD Kit&lt;/strong&gt;&lt;/a&gt;, a lightweight and performant library for creating drag and drop interfaces for Vue 3.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why did I create another drag and drop library? 🤔
&lt;/h2&gt;

&lt;p&gt;There are already several solutions for implementing drag and drop in the Vue ecosystem, but most of them are either outdated or have limitations in performance and flexibility. Inspired by the popular dnd-kit library for React, I decided to create a similar solution for Vue 3 that would be:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Lightweight&lt;/strong&gt; ☁️ — minimal impact on bundle size&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performant&lt;/strong&gt; ⚡ — optimized to work even with large lists&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexible&lt;/strong&gt; 🔄 — allows implementing virtually any drag and drop scenario&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accessible&lt;/strong&gt; ♿ — full support for keyboard navigation and screen readers&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Key Features of Vue DnD Kit ✨
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Simple API based on composable functions 📝
&lt;/h3&gt;

&lt;p&gt;The library provides an intuitive API based on Vue 3 composable functions:&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;useDraggable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useDroppable&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;@vue-dnd-kit/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// For draggable element&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;elementRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleDragStart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isDragging&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useDraggable&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// For droppable area&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;elementRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dropRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isOvered&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useDroppable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;onDrop&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Element dropped!&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Full keyboard navigation support ⌨️
&lt;/h3&gt;

&lt;p&gt;One of the unique features of the library is full keyboard control support:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;W A S D for moving elements&lt;/li&gt;
&lt;li&gt;Space/Enter for selecting and dropping&lt;/li&gt;
&lt;li&gt;Escape to cancel drag operation&lt;/li&gt;
&lt;li&gt;Tab for navigating between elements&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This makes your interfaces accessible to all users, including those who cannot use a mouse.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. High performance 🏎️
&lt;/h3&gt;

&lt;p&gt;Vue DnD Kit is optimized to work even with large lists and complex interfaces:&lt;br&gt;
&lt;a href="https://dev.tourl"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Minimal component re-renders&lt;/li&gt;
&lt;li&gt;Efficient DOM operations&lt;/li&gt;
&lt;li&gt;Prevention of memory leaks&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  4. Full customization 🎨
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Ability to change the container appearance&lt;/li&gt;
&lt;li&gt;Ability to change the appearance of the dragged element(s)&lt;/li&gt;
&lt;li&gt;Group support&lt;/li&gt;
&lt;li&gt;Ability to completely write your own element detection sensor where automatic element priority detection and its availability works under the hood (a zone may have groups into which the element(s) cannot enter)&lt;/li&gt;
&lt;li&gt;Throttle support with custom sensor&lt;/li&gt;
&lt;li&gt;Support for animations with both CSS and JS animation libraries like GSAP, Anime.js, etc.&lt;/li&gt;
&lt;li&gt;Ability to completely write your own drop logic&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Usage Examples 💻
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt; &lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ref&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;vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Draggable&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;./components/Draggable.vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Droppable&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;./components/Droppable.vue&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;elementInDropZone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&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;handleDrop&lt;/span&gt; &lt;span class="o"&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="nx"&gt;elementInDropZone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&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;handleEnd&lt;/span&gt; &lt;span class="o"&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="nx"&gt;elementInDropZone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&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;"container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Draggable&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"!elementInDropZone"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; Drag me! &lt;span class="nt"&gt;&amp;lt;/Draggable&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Droppable&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;drop=&lt;/span&gt;&lt;span class="s"&gt;"handleDrop"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Draggable&lt;/span&gt;
        &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"elementInDropZone"&lt;/span&gt;
        &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;end=&lt;/span&gt;&lt;span class="s"&gt;"handleEnd"&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        I'm in the drop zone!
      &lt;span class="nt"&gt;&amp;lt;/Draggable&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/Droppable&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  SortableList 📋
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Drag.vue
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt; &lt;span class="na"&gt;setup&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useDraggable&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;@vue-dnd-kit/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;computed&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;vue&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;props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;defineProps&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;any&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;number&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;elementRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleDragStart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isOvered&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useDraggable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&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="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt;
    &lt;span class="na"&gt;ref=&lt;/span&gt;&lt;span class="s"&gt;"elementRef"&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;pointerdown=&lt;/span&gt;&lt;span class="s"&gt;"handleDragStart"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;slot&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;hr&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"isOvered"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  List
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt; &lt;span class="na"&gt;setup&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DnDOperations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useDroppable&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;@vue-dnd-kit/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Drag&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;src/components/Drag.vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ref&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;vue&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;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Item 1&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="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Item 2&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="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Item 3&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;elementRef&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useDroppable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;onDrop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;DnDOperations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;applyTransfer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;ref=&lt;/span&gt;&lt;span class="s"&gt;"elementRef"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;TransitionGroup&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"sortable-list"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Drag&lt;/span&gt;
        &lt;span class="na"&gt;v-for=&lt;/span&gt;&lt;span class="s"&gt;"(item, index) in items"&lt;/span&gt;
        &lt;span class="na"&gt;:key=&lt;/span&gt;&lt;span class="s"&gt;"item.id"&lt;/span&gt;
        &lt;span class="na"&gt;:source=&lt;/span&gt;&lt;span class="s"&gt;"items"&lt;/span&gt;
        &lt;span class="na"&gt;:index=&lt;/span&gt;&lt;span class="s"&gt;"index"&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="si"&gt;}}&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/Drag&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/TransitionGroup&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;style&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nc"&gt;.sortable-list-move&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="nc"&gt;.sortable-list-enter-active&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="nc"&gt;.sortable-list-leave-active&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;all&lt;/span&gt; &lt;span class="m"&gt;0.5s&lt;/span&gt; &lt;span class="n"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nc"&gt;.sortable-list-enter-from&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="nc"&gt;.sortable-list-leave-to&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;opacity&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;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;30px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;style&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to start using 🚀
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Installation 📦
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#npm&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; @vue-dnd-kit/core @vueuse/core
&lt;span class="c"&gt;#yarn&lt;/span&gt;
yarn add @vue-dnd-kit/core @vueuse/core
&lt;span class="c"&gt;#pnpm&lt;/span&gt;
pnpm add @vue-dnd-kit/core @vueuse/core
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Adding as a plugin 🔌
&lt;/h3&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;createApp&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;vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;App&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;./App.vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;VueDnDKitPlugin&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;@vue-dnd-kit/core&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;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;VueDnDKitPlugin&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#app&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;h2&gt;
  
  
  Vue DnD Kit Ecosystem 🌐
&lt;/h2&gt;

&lt;p&gt;It's important to note that @vue-dnd-kit/core is just the main package of the library, which provides basic functionality for drag and drop. Additional packages are in development:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;@vue-dnd-kit/utils&lt;/strong&gt; 🛠️ — a set of utilities to simplify typical scenarios&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;@vue-dnd-kit/components&lt;/strong&gt; 🧩 — ready-made components for quick integration (sortable lists, kanban boards, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;@vue-dnd-kit/devtools&lt;/strong&gt; 🔍 — a plugin for Vue DevTools that will allow debugging the state of drag and drop in the application&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These additional packages will significantly simplify the development of complex drag and drop interfaces and will be released in the near future.&lt;/p&gt;

&lt;h2&gt;
  
  
  Future Plans 🔮
&lt;/h2&gt;

&lt;p&gt;This is just the beginning! Plans include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Creating additional packages with ready-made components for typical scenarios&lt;/li&gt;
&lt;li&gt;Improving documentation and examples&lt;/li&gt;
&lt;li&gt;Expanding capabilities for working with nested lists&lt;/li&gt;
&lt;li&gt;Optimizing for even higher performance&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Vue DnD Kit is my contribution to the Vue ecosystem, which I hope will help developers create more interactive and accessible interfaces. It currently has a stable API and will be improved in the future. Documentation has already been added but will be improved. The library is open source, and I welcome any contributions from the community!&lt;/p&gt;

&lt;p&gt;Repository: &lt;a href="https://github.com/zizigy/vue-dnd-kit" rel="noopener noreferrer"&gt;github.com/zizigy/vue-dnd-kit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Documentation: &lt;a href="https://zizigy.github.io/vue-dnd-kit" rel="noopener noreferrer"&gt;zizigy.github.io/vue-dnd-kit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I look forward to your feedback and suggestions in the comments!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>vue</category>
      <category>npm</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
