A comprehensive, accessible web components library built with modern web standards
π Documentation β’ π Live Examples β’ π¬ Community
- π¦ 22+ Production-Ready Components - From buttons to complex carousels
 - βΏ Accessibility First - ARIA attributes and keyboard navigation built-in
 - π¨ Tailwind CSS Integration - Beautiful, customizable styling out of the box
 - β‘ Alpine.js Powered - Reactive components with minimal JavaScript
 - π± Responsive Design - Mobile-first approach for all screen sizes
 - π§ Framework Agnostic - Works with React, Vue, Angular, or vanilla HTML
 - π§ͺ 100% Test Coverage - Comprehensive test suite with Mocha
 - π TypeScript Definitions - Full type support for better DX
 - π Modern Browser Support - ES6+ with graceful degradation
 
# npm
npm install dry2-web-components
# yarn
yarn add dry2-web-components
# pnpm
pnpm add dry2-web-components<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>DRY2 Example</title>
    
    <!-- Tailwind CSS -->
    <script src="https://cdn.tailwindcss.com"></script>
    
    <!-- Alpine.js -->
    <script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script>
</head>
<body>
    <!-- DRY2 Components -->
    <script type="module">
        import 'dry2-web-components/src/dry2/dry2.js';
        import 'dry2-web-components/src/dry2/button.js';
        import 'dry2-web-components/src/dry2/avatar.js';
        import 'dry2-web-components/src/dry2/toast.js';
    </script>
    <!-- Use the components -->
    <div class="container mx-auto p-6">
        <h1 class="text-3xl font-bold mb-6">Welcome to DRY2!</h1>
        
        <div class="space-y-4">
            <dry-button variant="primary" size="lg">
                Get Started
            </dry-button>
            
            <avatar-component 
                name="John Doe" 
                src="https://example.com/avatar.jpg"
                size="lg">
            </avatar-component>
        </div>
    </div>
    <script>
        // Use the Toast API
        document.querySelector('dry-button').addEventListener('click', () => {
            Toast.success('Welcome to DRY2 Web Components!');
        });
    </script>
</body>
</html><dry-button>- Versatile button with variants, sizes, loading states, and Font Awesome icons<toggle-switch>- Accessible toggle switch for boolean inputs<super-select-component>- Advanced select with search and multi-selection
<avatar-component>- User avatars with image fallbacks and initials<badge-component>- Status indicators and notification badges<card-component>- Flexible content containers<stat-component>- Statistical data display with trends
<breadcrumbs-component>- Hierarchical navigation breadcrumbs<tabs-component>- Tabbed interface with multiple variants
<dry-accordion>- Collapsible content sections<collapse-component>- Smooth height-based collapse animations<drawer-component>- Side panel/drawer with HTMX support
<carousel-component>- Touch-enabled carousel with autoplay<swap-component>- Icon/state swapping with transitions<countdown-component>- Flexible countdown timers
<chat-bubble>- Chat message bubbles with status indicators<toast-component>- Notification toasts with global API<dialog-component>- Modal dialogs with HTMX integration
<timeline-component>- Event timelines with custom styling<qr-component>- QR code generation with customization
<dry-code>- Syntax-highlighted code blocks with copy-to-clipboard functionality<wysiwyg-component>- Rich text editor with HTML sanitization
DRY2 components are built with Tailwind CSS and support extensive customization:
<!-- Custom button styling -->
<dry-button 
    variant="primary"
    size="lg"
    class="shadow-xl hover:shadow-2xl transform hover:scale-105">
    Custom Styled Button
</dry-button>
<!-- Button with Font Awesome icon -->
<dry-button 
    variant="primary"
    icon="fas fa-plus">
    Add Item
</dry-button>
<!-- Icon-only button -->
<dry-button 
    variant="outline"
    icon="fas fa-download">
</dry-button>
<!-- Custom avatar colors -->
<avatar-component 
    name="Jane Doe"
    style="--avatar-bg: #8b5cf6; --avatar-text: white;">
</avatar-component>Many components support CSS custom properties for deep customization:
:root {
    --dry-primary-color: #6366f1;
    --dry-secondary-color: #64748b;
    --dry-success-color: #10b981;
    --dry-warning-color: #f59e0b;
    --dry-error-color: #ef4444;
    --dry-border-radius: 0.5rem;
    --dry-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}DRY2 comes with comprehensive test coverage using Mocha and Chai:
# Run all tests
npm test
# Run tests with coverage
npm run test:coverage
# Run tests in watch mode
npm run test:watch
# Run tests for specific component
npm run test:component buttonExample test for a custom component:
import { expect } from 'chai';
import '../setup.js';
describe('My Custom Component', () => {
    let element;
    beforeEach(() => {
        element = document.createElement('my-component');
        document.body.appendChild(element);
    });
    it('should create element', async () => {
        await waitForComponent(element);
        expect(element).to.exist;
    });
    it('should respond to attribute changes', async () => {
        element.setAttribute('variant', 'primary');
        await waitForComponent(element);
        
        const button = element.querySelector('.button');
        expect(button.className).to.include('bg-blue-600');
    });
});You can create custom components by extending HTMLElement:
class MyComponent extends HTMLElement {
    static get observedAttributes() {
        return ['variant', 'size', 'disabled'];
    }
    render() {
        return `
            <div class="${this.getComponentClasses()}">
                ${this.renderSlot('default', 'Default content')}
            </div>
        `;
    }
    getComponentClasses() {
        return `component ${this.variant} ${this.size}`;
    }
}// Reactive state
this.setState({ loading: true });
// Listen to state changes automatically triggers re-render// Emit custom events
this.emit('component:change', { value: newValue });
// Listen to events
element.addEventListener('component:change', (event) => {
    console.log('New value:', event.detail.value);
});// Built-in accessibility helpers
this.setAttribute('aria-label', 'Button description');
this.setAttribute('role', 'button');
this.setAttribute('tabindex', '0');import { useEffect, useRef } from 'react';
import 'dry2-web-components/button';
function MyReactComponent() {
    const buttonRef = useRef();
    useEffect(() => {
        const button = buttonRef.current;
        
        const handleClick = (event) => {
            console.log('Button clicked:', event.detail);
        };
        button.addEventListener('button:click', handleClick);
        return () => button.removeEventListener('button:click', handleClick);
    }, []);
    return (
        <dry-button 
            ref={buttonRef}
            variant="primary"
            size="lg">
            React Button
        </dry-button>
    );
}<template>
    <dry-button 
        :variant="variant"
        :size="size"
        @button:click="handleClick">
        Vue Button
    </dry-button>
</template>
<script>
import 'dry2-web-components/button';
export default {
    data() {
        return {
            variant: 'primary',
            size: 'lg'
        };
    },
    methods: {
        handleClick(event) {
            console.log('Button clicked:', event.detail);
        }
    }
};
</script>import { Component, ElementRef, ViewChild } from '@angular/core';
import 'dry2-web-components/button';
@Component({
    selector: 'app-button',
    template: `
        <dry-button 
            #buttonElement
            [attr.variant]="variant"
            [attr.size]="size"
            (button:click)="handleClick($event)">
            Angular Button
        </dry-button>
    `
})
export class ButtonComponent {
    @ViewChild('buttonElement') buttonElement!: ElementRef;
    
    variant = 'primary';
    size = 'lg';
    handleClick(event: CustomEvent) {
        console.log('Button clicked:', event.detail);
    }
}All components are mobile-first and responsive:
<!-- Responsive button sizes -->
<dry-button 
    size="sm"
    class="md:size-md lg:size-lg">
    Responsive Button
</dry-button>
<!-- Responsive avatar -->
<avatar-component
    size="sm"
    class="md:size-md lg:size-lg"
    name="John Doe">
</avatar-component>| Component | Gzipped Size | Dependencies | 
|---|---|---|
| Base | 2.1 KB | None | 
| Button | 1.8 KB | Alpine.js | 
| Avatar | 1.5 KB | Alpine.js | 
| Toast | 2.3 KB | None | 
| Carousel | 3.2 KB | Alpine.js | 
// Load components on demand
const loadButton = () => import('dry2-web-components/button');
const loadCarousel = () => import('dry2-web-components/carousel');
// Load when needed
if (needsCarousel) {
    await loadCarousel();
}| Browser | Version | Support | 
|---|---|---|
| Chrome | 73+ | β Full | 
| Firefox | 63+ | β Full | 
| Safari | 12.1+ | β Full | 
| Edge | 79+ | β Full | 
For older browsers, include these polyfills:
<script src="https://unpkg.com/@webcomponents/webcomponentsjs@2/webcomponents-loader.js"></script>
<script src="https://unpkg.com/proxy-polyfill@0.3.2/proxy.min.js"></script>We welcome contributions! Please see our Contributing Guide for details.
# Clone the repository
git clone https://github.com/yourusername/dry2-web-components.git
cd dry2-web-components
# Install dependencies
npm install
# Start development server
npm run dev
# Run tests
npm test
# Build for production
npm run build- Create component file in 
src/dry2/ - Add comprehensive tests in 
test/components/ - Create showcase page in 
examples/ - Update documentation
 - Submit pull request
 
MIT License - see the LICENSE file for details.
- Alpine.js for reactive functionality
 - Tailwind CSS for utility-first styling
 - Web Components for the foundation
 - Mocha and Chai for testing
 
- π Documentation
 - π¬ GitHub Discussions
 - π Issue Tracker
 - π§ Email Support
 
β Star us on GitHub β it helps the project grow!
Made with β€οΈ by the DRY2 team