Building Glass UI: A Modern, Interactive, and Accessible React Component Library
Creating a component library from scratch that is not only beautiful but also highly accessible and developer-friendly is a rewarding challenge. In this post, I want to introduce Glass UI — a modern React component library built with best-in-class tools and robust theming capabilities.
If you’ve ever wanted to seamlessly integrate Glassmorphism into your web applications while keeping your bundle small and your components accessible, this library is for you.
🛠️ The Tech Stack
To ensure that Glass UI is blazing fast, easy to maintain, and a joy to use, I chose a very specific stack:
- React & TypeScript: For type safety and robust component interfaces.
- Tailwind CSS: For utility-first styling. We heavily utilize CSS variables (
var(--primary),var(--background)) to power our dynamic Light, Dark, and Glass themes. - Vite: Our lightning-fast build tool and bundler. It compiles our library down to both ES Modules and UMD formats.
- Storybook (v8): The ultimate workspace for building, documenting, and testing UI components in isolation.
- Lucide React: Beautiful, consistent, and lightweight SVG icons.
✨ Standout Features
1. The Tri-Theme System (Light, Dark, Glass)
Most component libraries stop at Light and Dark modes. Glass UI introduces a third primary theme tier: Glass. The glass variant utilizes modern CSS backdrop filters (backdrop-blur) to create a "frosted glass" effect. When placed over colorful layouts, complex imagery, or dynamic backgrounds, Glass components elegantly blend in while remaining perfectly legible.
2. 10 Core Components Out of the Box
I built the 10 most essential components you need to get a modern app off the ground:
Core:
- Button
- Input
- Switch
Display:
- Card
- Badge
- Avatar
- Progress-Bar
- Alert
Interactive:
- Modal
- Tabs
3. Rigorous WAI-ARIA Accessibility
Beautiful UI shouldn’t exclude users. Every single component in Glass UI is fully accessible:
- Screen Readers: We use dynamic IDs (
useId) to linkaria-describedbyandaria-labelledbyattributes. - Keyboard Navigation: Interactive elements use proper
tabIndexattributes and semantic roles (role="tab",role="switch",role="alert"). - Automated ADA Auditing: Our Storybook workspace includes
@storybook/addon-a11y, which runs live Web Content Accessibility Guidelines (WCAG) checks directly in the browser during development.
🧪 Bulletproof QA: Headless E2E Component Testing
Writing
Get _Khussshal_’s stories in your inbox
Join Medium for free to get updates from this writer.
play functions in Storybook is great for visual documentation, but what about Continuous Integration (CI)? We need to ensure that our component interactions never break when we push new code.
To solve this, I integrated Playwright via the official @storybook/test-runner.
Instead of writing a completely separate suite of unit tests, the test runner automatically spins up a headless Chromium browser, cycles through every single component story, and executes the
play functions (clicks, typing, assertions) at lightning speed.
Here is the exact setup used to achieve this zero-config, concurrent testing environment:
1. Install Dependencies
npm install @storybook/test-runner playwright concurrently wait-on http-server --save-dev2. The Golden NPM Script
I added a powerful, concurrent command to the
package.json that handles the entire testing lifecycle — building the static Storybook, spinning up a local server, waiting for the port to open, and executing Playwrigh
"test-storybook": "concurrently -k -s first -n \"SB,TEST\" -c \"magenta,blue\" \"npx http-server storybook-static --port 6006 --silent\" \"wait-on tcp:6006 && test-storybook\""Now, with a single npm run test-storybook command, the suite verifies all 48 component states instantly. Zero flakiness, maximum confidence.
🚀 How to Publish a Vite + React Component Library to NPM
Ready to share your own library with the world? Here is the exact step-by-step process I used to publish Glass UI.
Step 1: Configure Vite for Library Mode
We configured
vite.config.ts to output our components instead of a standard web app:
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import dts from 'vite-plugin-dts';
import { resolve } from 'path';
export default defineConfig({
plugins: [react(), dts({ insertTypesEntry: true })],
build: {
lib: {
entry: resolve(__dirname, 'src/index.ts'),
name: 'GlassUI',
formats: ['es', 'umd'],
fileName: (format) => `glass-ui.${format}.js`
},
rollupOptions: {
external: ['react', 'react-dom', 'react/jsx-runtime'],
output: {
globals: { react: 'React', 'react-dom': 'ReactDOM' },
assetFileNames: "style.css"
}
}
}
});Step 2: Prepare package.json
Ensure your
package.json points to the correct dist files and sets your Peer Dependencies.
{
"name": "glass-ui",
"version": "1.0.0",
"type": "module",
"main": "dist/glass-ui.umd.js",
"module": "dist/glass-ui.es.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"import": "./dist/glass-ui.es.js",
"require": "./dist/glass-ui.umd.js"
},
"./style.css": "./dist/style.css"
},
"files": ["dist"],
"peerDependencies": {
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
},
"scripts": {
"build": "tsc -b && vite build",
"prepublishOnly": "npm run build"
}
}Note: Adding "prepublishOnly": "npm run build" ensures you never accidentally publish stale code!
Step 3: Login to NPM
Open your terminal and authenticate with NPM. (If you don’t have an account, create one at npmjs.com).
npm loginStep 4: Publish to the Public Registry
If your package name is available, simply run:
npm publish --access publicBoom! 🎊 Your package is now live on the NPM registry. Anyone can install it via npm install glass-ui.
Final Thoughts
Building a component library from scratch forces you to think deeply about API design, accessibility, and theming architectures. With Vite, Tailwind, and Storybook, the developer experience is simply unparalleled.
Check out the code on GitHub and let me know what you think of the Glassmorphism approach!
Happy Coding! 💻✨









