Headless React component library focused on performance (INP) and accessibility. Built with TypeScript, minimal CSS, and designed for modern React applications.
- 🚀 Performance-first: Optimized for Interaction to Next Paint (INP)
- ♿ Accessible: Full ARIA support, keyboard navigation, and focus management
- 🎨 Headless: Minimal styling, easily customizable with Tailwind CSS or your own styles
- 📦 Tree-shakeable: Import only what you need
- 💪 TypeScript: Full type safety with comprehensive type definitions
- ⚡ Modern: Built with React 18+ and Vite
npm install @travelopia/react-componentsyarn add @travelopia/react-componentspnpm add @travelopia/react-componentsA fully accessible modal dialog component with focus management, body scroll lock, and portal rendering.
import { TpModal, TpModalOverlay, TpModalContent, TpModalTitle } from '@travelopia/react-components'
<TpModal open={isOpen} onOpenChange={setIsOpen}>
<TpModalOverlay>
<TpModalContent>
<TpModalTitle>Modal Title</TpModalTitle>
<p>Modal content goes here</p>
</TpModalContent>
</TpModalOverlay>
</TpModal>Features:
- ✅ Controlled and uncontrolled modes
- ✅ Focus trap and focus management
- ✅ Body scroll lock
- ✅ Keyboard navigation (Escape to close)
- ✅ Click outside to close
- ✅ Portal rendering
View full TpModal documentation →
INP-optimized accordion component with CSS Grid animations and SEO-friendly architecture.
import { TpAccordion, TpAccordionItem, TpAccordionTrigger, TpAccordionContent } from '@travelopia/react-components'
<TpAccordion defaultValue={['item-1']}>
<TpAccordionItem value="item-1">
<TpAccordionTrigger>Question 1</TpAccordionTrigger>
<TpAccordionContent>
<p>Answer 1</p>
</TpAccordionContent>
</TpAccordionItem>
</TpAccordion>Features:
- ✅ INP-optimized with
useCallbackWithYield - ✅ SEO-friendly (content always in DOM)
- ✅ CSS Grid animations (200ms, hardware-accelerated)
- ✅ Multiple items can be open simultaneously
- ✅ Controlled and uncontrolled modes
- ✅ WAI-ARIA compliant
View full TpAccordion documentation →
Manage modal state imperatively.
import { useTpModal } from '@travelopia/react-components'
const modal = useTpModal()
// Returns: { isOpen, open, close, toggle }Manage accordion state imperatively.
import { useTpAccordion } from '@travelopia/react-components'
const accordion = useTpAccordion(['item-1'])
// Returns: { value, openItem, closeItem, toggleItem, openAll, closeAll }All components are headless with minimal structural CSS. Style them with:
<TpModalContent className="bg-white rounded-lg shadow-xl p-6 max-w-md">
<TpModalTitle className="text-2xl font-bold mb-4">
Styled Modal
</TpModalTitle>
</TpModalContent>import styles from './MyModal.module.css'
<TpModalContent className={styles.modal}>
<TpModalTitle className={styles.title}>
Styled Modal
</TpModalTitle>
</TpModalContent><TpModalContent className="my-modal">
<TpModalTitle className="my-title">
Styled Modal
</TpModalTitle>
</TpModalContent>Full TypeScript support with exported types:
import type {
TpModalProps,
TpAccordionProps,
UseTpModalReturn,
UseTpAccordionReturn,
} from '@travelopia/react-components'- React 18+
- Modern browsers (Chrome, Firefox, Safari, Edge)
- CSS Grid support required for TpAccordion animations
# Install dependencies
npm install
# Run tests
npm test
# Run tests in watch mode
npm test -- --watch
# Run tests with coverage
npm run test:coverage
# Build library
npm run build
# Lint
npm run lint
# Format
npm run format
# Type check
npm run typecheckMIT
Contributions are welcome! Please read our contributing guidelines before submitting PRs.
Built with ❤️ by Travelopia
Inspired by Travelopia Web Components