Tailwind Component Library
A collection of 80+ production-ready, accessible UI components built with Tailwind CSS and React. Every component follows WAI-ARIA authoring practices, supports keyboard navigation, includes dark mode variants, and is fully typed with TypeScript. Covers the full spectrum of application UI: form controls, data tables with sorting and pagination, modal dialogs, navigation patterns, card layouts, data visualization widgets, and feedback components. Copy-paste into your project and customize with Tailwind's utility classes — no component library dependency, no CSS-in-JS runtime, just clean markup you own.
Key Features
- 80+ Components — Forms, tables, modals, navigation, cards, alerts, badges, avatars, tooltips, and data visualization
-
Fully Accessible — WAI-ARIA compliant with proper
role,aria-*attributes, focus management, and keyboard navigation -
Dark Mode Built In — Every component includes
dark:variants that respect the systemprefers-color-scheme -
TypeScript Strict — Complete prop types with discriminated unions, generic components, and
asprop polymorphism - Responsive by Default — Mobile-first design with breakpoint-aware layouts; tested at 320px, 768px, and 1280px
- No Dependencies — Pure Tailwind CSS utility classes + React; no Radix, Headless UI, or runtime CSS required
-
Composable Architecture — Compound component patterns (e.g.,
<Table>,<Table.Header>,<Table.Row>) for maximum flexibility - Copy-Paste Ready — Each component is a single file; no build step, no package to install, just copy and use
Quick Start
- Ensure Tailwind CSS is installed in your project:
npm install -D tailwindcss @tailwindcss/forms @tailwindcss/typography
Copy components from
components/into your project's component directory.Import and use immediately:
import { Button } from '@/components/ui/Button';
import { Modal } from '@/components/ui/Modal';
import { DataTable } from '@/components/ui/DataTable';
export function UserManagement() {
const [isOpen, setIsOpen] = useState(false);
return (
<>
<Button variant="primary" size="md" onClick={() => setIsOpen(true)}>
Add User
</Button>
<Modal isOpen={isOpen} onClose={() => setIsOpen(false)} title="Add New User">
<UserForm onSubmit={(data) => { /* handle */ }} />
</Modal>
<DataTable
columns={columns}
data={users}
sortable
paginated
pageSize={10}
/>
</>
);
}
Architecture / How It Works
tailwind-component-library/
├── components/
│ ├── forms/ # Button, Input, Select, Checkbox, RadioGroup, Toggle, Textarea, FileUpload
│ ├── data-display/ # DataTable, Card, Badge, Avatar, Stat, Timeline
│ ├── feedback/ # Alert, Toast, Progress, Skeleton, EmptyState
│ ├── navigation/ # Sidebar, Breadcrumb, Tabs, Pagination, CommandPalette
│ ├── overlays/ # Modal, Dropdown, Tooltip, Popover
│ └── layout/ # Container, Grid, Divider
└── theme/ # Extended tailwind.config.ts with design tokens
Usage Examples
Button Component with Variants
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'primary' | 'secondary' | 'ghost' | 'danger';
size?: 'sm' | 'md' | 'lg';
loading?: boolean;
}
const styles = {
primary: 'bg-blue-600 text-white hover:bg-blue-700 focus-visible:ring-blue-500',
secondary: 'bg-gray-100 text-gray-900 hover:bg-gray-200 dark:bg-gray-800 dark:text-gray-100',
ghost: 'text-gray-600 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-800',
danger: 'bg-red-600 text-white hover:bg-red-700 focus-visible:ring-red-500',
} as const;
const sizes = { sm: 'px-3 py-1.5 text-sm', md: 'px-4 py-2 text-sm', lg: 'px-6 py-3 text-base' } as const;
export function Button({ variant = 'primary', size = 'md', loading, children, ...props }: ButtonProps) {
return (
<button className={`inline-flex items-center justify-center rounded-lg font-medium transition-colors
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2
disabled:pointer-events-none disabled:opacity-50 ${styles[variant]} ${sizes[size]}`}
disabled={loading || props.disabled} {...props}>
{loading && <Spinner className="mr-2 h-4 w-4" />}
{children}
</button>
);
}
Accessible Modal
export function Modal({ isOpen, onClose, title, children }: ModalProps) {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
const handler = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); };
if (isOpen) document.addEventListener('keydown', handler);
return () => document.removeEventListener('keydown', handler);
}, [isOpen, onClose]);
if (!isOpen) return null;
return (
<div ref={ref} className="fixed inset-0 z-50 flex items-center justify-center bg-black/50"
onClick={(e) => e.target === ref.current && onClose()} role="dialog" aria-modal="true" aria-labelledby="modal-title">
<div className="w-full max-w-lg rounded-xl bg-white p-6 shadow-xl dark:bg-gray-900">
<h2 id="modal-title" className="text-lg font-semibold">{title}</h2>
<div className="mt-4">{children}</div>
</div>
</div>
);
}
Configuration
Extended Tailwind Theme
import type { Config } from 'tailwindcss';
export default {
darkMode: 'class',
content: ['./src/**/*.{ts,tsx}'],
theme: {
extend: {
colors: { brand: { 50: '#eff6ff', 500: '#3b82f6', 600: '#2563eb', 700: '#1d4ed8' } },
animation: { 'slide-in': 'slideIn 0.2s ease-out', 'fade-in': 'fadeIn 0.15s ease-out' },
keyframes: {
slideIn: { from: { transform: 'translateY(-8px)', opacity: '0' }, to: { transform: 'translateY(0)', opacity: '1' } },
fadeIn: { from: { opacity: '0' }, to: { opacity: '1' } },
},
},
},
plugins: [require('@tailwindcss/forms'), require('@tailwindcss/typography')],
} satisfies Config;
Best Practices
-
Use
focus-visiblenotfocus—focus-visibleonly shows focus rings on keyboard navigation, not mouse clicks -
Always add
dark:variants — even if you don't support dark mode yet; it's much harder to retrofit later -
Prefer
aria-labelledbyoveraria-label— visible labels are better for accessibility and maintenance -
Use compound components for complex UI —
<Tabs><Tabs.List><Tabs.Tab>is more flexible than a single<Tabs items={[]}> -
Set
disabled:pointer-events-none— prevents click events on disabled buttons that only change opacity -
Use
ringutilities for focus indicators —focus-visible:ring-2 focus-visible:ring-offset-2gives consistent, visible focus
Troubleshooting
| Issue | Cause | Fix |
|---|---|---|
| Tailwind classes not applying | Component files not in content array |
Add component directory to content in tailwind.config.ts
|
| Dark mode doesn't toggle | Using media strategy instead of class
|
Set darkMode: 'class' and toggle .dark class on <html>
|
| Modal doesn't close on backdrop click | Click event targets child elements | Compare e.target === overlayRef.current to detect backdrop clicks |
| Dropdown menu clips at viewport edge | Absolute positioning without boundary check | Use position: fixed with viewport boundary calculations |
This is 1 of 11 resources in the Frontend Developer Pro toolkit. Get the complete [Tailwind Component Library] with all files, templates, and documentation for $39.
Or grab the entire Frontend Developer Pro bundle (11 products) for $129 — save 30%.
Top comments (0)