Article

I Built a Vue 3 Component Library: Airo UI, Light as Air

Most Vue component libraries out there are either so bloated that you use ten components but have to carry the whole universe, or so stiff in motion that it feels like flipping through PowerPoint slides. Airo UI is a Vue 3 component library I built from scratch: spring animations, restrained colors, a 4px grid, full TypeScript types, and 30+ components imported on demand. This article covers where it came from, the design trade-offs I made, and how you can get it running in three lines of code.

image.png

Why Reinvent the Wheel

The Pain Points of Existing Component Libraries

Element Plus, Ant Design Vue, Naive UI: all good stuff, but what they solve is the problem of "enterprise admin systems". When I wanted to build a personal project, a small tool, or a blog theme, the visual language of these libraries was too heavy. Buttons with drop shadows, forms with borders, dialogs with mask layers: every component shouts "I'm professional", but all I wanted was "quiet".

Another problem is motion. The vast majority of component libraries use ease-in-out or linear for state changes: mechanical, predictable, lifeless. A Checkbox tick appears instantly, a Switch slides at constant speed, a Modal scales up linearly. These interactions aren't wrong, but they hold no surprises either.

What I Wanted

A sufficiently restrained component library: neutral colors as the base with just one accent color; all motion using spring curves, so interactions feel "bouncy" rather than "slidey"; spacing strictly on a 4px grid, with no tweaking pixels by feel; TypeScript covering every Prop and Emit, so you don't need to look up docs while coding in the IDE.

Simply put: a component library written for my own use, open-sourced along the way.


Design Principles

1. Spring Easing Curves

All state transitions use a spring curve uniformly, rejecting ease-in-out. A Checkbox has a tiny bounce when ticked, a Switch has a slight overshoot when it slides in, and a Slider has inertial decay after you let go of the drag. These details are unremarkable on their own, but taken together, you'll feel the interface is "alive".

It's implemented with a single cubic-bezier:

--ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);

The value 1.56 makes the curve overshoot once near the endpoint, which visually reads as a "bounce". No JavaScript animation library is needed; pure CSS does it.

2. Restrained Colors

The whole set of components has just one accent color, and the rest are all neutral grays. The default accent color is #9ebbde, a quiet misty blue. Don't like it? Override one CSS variable:

:root {
  --color-accent: #34C796;
}

No need to fork the source, no config files, no Sass variable compilation. One line does global theming.

3. The 4px Grid System

Spacing relies on a system, not on feel. From --space-1 (4px) to --space-12 (48px), the padding, margin, and gap of every component are all chosen from these 12 values. Visually there's a hard-to-describe sense of "alignment", and that's grid rhythm.


Up and Running in Three Lines of Code

Install:

npm install @airo-ui/vue
# or
pnpm add @airo-ui/vue

Register in the entry file:

import { createApp } from 'vue'
import AiroUI from '@airo-ui/vue'
import '@airo-ui/vue/dist/style.css'

const app = createApp(App)
app.use(AiroUI)
app.mount('#app')

Use directly in components:

<template>
  <AButton variant="primary">Confirm</AButton>
  <ASwitch v-model="darkMode" />
  <ASlider v-model="volume" />
</template>

On-demand imports are supported as well. ESM modular bundling, tree-shaking friendly: use only Switch and only Switch gets bundled, with the other components dropped automatically.


Component List

30+ components in five categories:

Category

Components

Form basics

Button, Input, Textarea, Checkbox, Radio, Switch, Select, Slider, Segmented

Feedback & notifications

Toast, Modal, Tooltip, Drawer, Progress, Skeleton

Data display

Tag, Card, Badge, Tabs, Avatar, Divider, Pagination

Composite components

ChatInput, Search

Desktop topics

DropdownMenu, Table, Tree

Every component has complete TypeScript type definitions: Props, Emits, and Slots are all exported in full, and IDE IntelliSense works out of the box.


Accessibility and Browser Support

WCAG 2.1 AA: all interactive components are annotated with the correct ARIA roles and states. Switch has role="switch" and aria-checked, is keyboard reachable (Tab to focus, Space to toggle), and screen readers announce state changes correctly.

On animation, the components respond to the prefers-reduced-motion media query: after a user turns off animations in system settings, all spring effects automatically degrade to instant switching.

Browser support: the latest two major versions of Chrome, Firefox, Safari, and Edge.


Airo UI started out only to solve my own problem: I wanted a set of Vue 3 components with a quiet look, comfortable motion, and complete types, for use in my personal projects and small tools. As I kept going, things piled up, so I simply organized it into a complete component library and open-sourced it.

The name comes from Air. It should feel like air to use: it's there, but you don't consciously notice it. Components shouldn't steal your product's thunder; they should sit there quietly and leave the stage to your content.

If you're also looking for a lightweight, restrained Vue 3 component library with elastic animations, give it a try.

Bug reports and feature suggestions are welcome directly in GitHub Issues. If you find it helpful, a Star is the best support.

Copyright

Copyright of this article belongs to its author or rights holder. When reposting or quoting, please follow the applicable license terms and keep the link to this article.

Link to this article: https://xuyi.dev/en/2026-05-19-avqlwc