rupoui
v1.6.1
Published
A modern, accessible, and highly customizable React UI library built with **Tailwind CSS** and **Framer Motion**. RupoUI focuses on developer experience, providing a type-safe, consistent, and motion-first design system.
Readme
RupoUI
A modern, accessible, and highly customizable React UI library built with Tailwind CSS and Framer Motion. RupoUI focuses on developer experience, providing a type-safe, consistent, and motion-first design system.
🚀 Features
- Tailwind CSS First: Built on top of Tailwind CSS for infinite customization.
- Type-Safe: Written in TypeScript with full type definitions.
- Accessible: Follows ARIA patterns and best practices.
- Motion: Integrated animations using Framer Motion.
- Customizable: Global configuration and granular slot styling via
classNames. - Dark Mode: Native support for dark mode.
📦 Installation
npm install rupoui framer-motion
# or
pnpm add rupoui framer-motion🛠 Setup
1. Configure Tailwind CSS
Add the RupoUI plugin to your tailwind.config.js or tailwind.config.ts content and plugins:
// tailwind.config.ts
import { rupoUI } from "rupoui/tailwind";
export default {
content: [
// ... your app content
"./node_modules/rupoui/dist/**/*.{js,ts,jsx,tsx}",
],
plugins: [rupoUI()],
};2. Add Provider
Wrap your application with the RupoUIProvider to enable global configurations and styles:
import { RupoUIProvider } from "rupoui";
function App() {
return (
<RupoUIProvider>
<YourApp />
</RupoUIProvider>
);
}🎨 Theming & Semantic Colors
RupoUI uses a semantic color system powered by CSS variables, allowing you to theme your application without changing component class names. Themes work seamlessly with standard Tailwind utility classes.
Configuration
Customize themes in your tailwind.config.ts:
// tailwind.config.ts
import { rupoUI } from "rupoui/tailwind";
export default {
plugins: [
rupoUI({
themes: {
light: {
colors: {
background: "#ffffff",
foreground: "#111111",
primary: {
DEFAULT: "#2563eb",
foreground: "#ffffff",
},
},
},
},
}),
],
};Usage
Use semantic color tokens directly in your components:
<button className="bg-primary text-primary-foreground px-4 py-2 rounded-md">Themed Button</button>Custom Themes
Apply custom themes using the data-theme attribute:
<html data-theme="dark"></html>🧩 Components
Card
A composable container component for grouping related content, with support for layout slots and interactive states.
import { Button, Card, CardBody, CardFooter, CardHeader } from "rupoui";
<Card radius="lg" shadow="sm" className="max-w-md">
<CardHeader>
<h4 className="text-lg font-semibold">Pro Plan</h4>
<p className="text-default-500 text-sm">Best for growing teams</p>
</CardHeader>
<CardBody>Includes analytics, priority support, and team collaboration features.</CardBody>
<CardFooter>
<Button fullWidth>Upgrade</Button>
</CardFooter>
</Card>;Props:
variant:solid(default),bordered,flat,ghost.color:default(default),primary,secondary,success,warning,danger.radius:none,sm,md,lg,xl.shadow:none(default),sm,md,lg.isHoverable,isClickable,isBlurred,fullWidth.classNames: Slot styling forbase,header,body,footer.
Accordion
An accessible disclosure component for showing and hiding sections of content.
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "rupoui";
<Accordion type="single" defaultValue="item-1" collapsible>
<AccordionItem value="item-1">
<AccordionTrigger>What is RupoUI?</AccordionTrigger>
<AccordionContent>
RupoUI is a modern React UI library built with TypeScript and Tailwind CSS.
</AccordionContent>
</AccordionItem>
<AccordionItem value="item-2">
<AccordionTrigger>Is it accessible?</AccordionTrigger>
<AccordionContent>
Yes. It supports ARIA semantics and keyboard interactions out of the box.
</AccordionContent>
</AccordionItem>
</Accordion>;Props (Accordion):
type:single(default) ormultiple.value/defaultValue/onValueChange: Controlled and uncontrolled state.collapsible: Allows closing the open item insinglemode.variant:solid(default),bordered,ghost.color:default(default),primary,secondary,success,warning,danger.radius:none,sm,md(default),lg,xl.fullWidth: Whether the accordion takes full width.classNames: Slot styling forbase,item,trigger,triggerInner,indicator,content,contentInner.
Accessibility & Keyboard:
- ARIA:
aria-expanded,aria-controls,role="region",aria-labelledby. - Keyboard:
Enter/Spaceto toggle,ArrowUp/ArrowDownto move focus,Home/Endfor first/last trigger.
Button
A versatile button component with support for loading states and icons.
import { Button } from "rupoui";
<Button variant="solid" color="primary">
Click Me
</Button>
<Button isLoading color="secondary">
Loading
</Button>
<Button variant="bordered" radius="full">
Bordered
</Button>Props:
variant:solid(default),bordered,flat,ghost,light.color:primary,secondary,success,warning,danger.size:sm,md(default),lg.radius:none,sm,md,lg,full.isLoading: Shows a spinner and disables interaction.
Input
A text input component with validation and content slots.
import { Input } from "rupoui";
<Input label="Email" placeholder="Enter your email" />
<Input
label="Password"
type="password"
isInvalid
errorMessage="Invalid password"
/>
<Input
startContent={<MailIcon />}
variant="bordered"
/>Props:
label,placeholder,description,errorMessage.startContent,endContent: Elements to display inside the input.isInvalid,isDisabled,isReadOnly.
InputNumber
A specialized numeric input with formatting, validation, and layout controls.
import { InputNumber } from "rupoui";
// Basic
<InputNumber label="Quantity" min={0} max={100} />
// Formatted Currency
<InputNumber
label="Price"
format={{
style: "currency",
currency: "USD",
prefix: "$"
}}
defaultValue={1000}
/>
// Stacked Controls
<InputNumber
label="Stacked"
controlsPosition="right"
/>Features:
- Controlled/Uncontrolled: Supports both
value/onChangeanddefaultValue. - Formatting: Format on blur via
formatprop (locale, precision, thousand separators, prefix/suffix). - Layout:
controlsPositionprop forsplit(default) orright(vertical stack). - Validation:
min,max,stepclamping.
DatePicker
A component that allows users to select a date from a calendar or enter it manually. It fully supports the standard Field API (labels, descriptions, error messages) and multiple input modes.
import { DatePicker } from "rupoui";
// Basic with Label
<DatePicker label="Birth Date" />
// Segmented Input (Date Only)
<DatePicker
label="Date of Birth"
inputMode="segmented"
description="DD / MM / YYYY"
/>
// Date + Time (Minutes)
<DatePicker
label="Appointment"
granularity="minute"
/>
// With Validation
<DatePicker
label="Event Date"
isInvalid
errorMessage="Please select a valid date."
/>
// Custom Styling
<DatePicker
label="Bordered"
variant="bordered"
color="secondary"
/>Props:
- Field Props:
label,description,errorMessage,fullWidth. - State:
value,defaultValue,onChange. - Validation:
isInvalid,isDisabled,isReadOnly,minDate,maxDate,disabledDates. - Input Mode:
inputMode:text(default) orsegmented.granularity:date(default),minute,second.
- Formatting:
format(e.g., "yyyy-MM-dd"),locale. - Style Variants:
variant:solid(default),bordered,flat,ghost.color:primary,secondary,success,warning,danger.size:sm,md,lg.radius:sm,md,lg,full.
DateRangePicker
A component for selecting a start and end date with calendar range selection, hover preview, and text/segmented input modes.
import { useState } from "react";
import { DateRangePicker } from "rupoui";
const Example = () => {
const [range, setRange] = useState<{ start: Date | null; end: Date | null }>({
start: null,
end: null,
});
return (
<>
<DateRangePicker label="Travel Dates" value={range} onChange={setRange} />
<DateRangePicker
label="Range (Two Inputs)"
rangeInput="two"
value={range}
onChange={setRange}
/>
<DateRangePicker
label="Range (Segmented)"
inputMode="segmented"
value={range}
onChange={setRange}
/>
</>
);
};Props:
- Field Props:
label,description,errorMessage,fullWidth. - State:
value,defaultValue,onChange. - Validation:
isInvalid,isDisabled,isReadOnly,minDate,maxDate,disabledDates. - Range Input:
rangeInput:single(default) ortwo.separator: separator string between start/end (default:–).inputMode:text(default) orsegmented.
- Style Variants:
variant:solid(default),bordered,flat,ghost.color:primary,secondary,success,warning,danger.size:sm,md,lg.radius:sm,md,lg,full.
Calendar
A pure renderer component for displaying a calendar grid with full keyboard navigation and accessibility support. It uses an external engine for state management, giving you complete control over the calendar's behavior.
import { addMonths, startOfMonth, subMonths } from "date-fns";
import { useMemo, useState } from "react";
import { Calendar, CalendarEngine, getMonthGrid } from "rupoui";
// Basic Calendar
function useCalendarEngine() {
const [viewDate, setViewDate] = useState(startOfMonth(new Date()));
const [selectedDate, setSelectedDate] = useState<Date>();
const weeks = useMemo(() => {
const grid = getMonthGrid({ viewDate, selectedDate });
return grid.map((row) =>
row.map((cell) => ({
...cell,
isOutsideMonth: !cell.isCurrentMonth,
})),
);
}, [viewDate, selectedDate]);
return {
weeks,
viewDate,
goToNextMonth: () => setViewDate((prev) => addMonths(prev, 1)),
goToPrevMonth: () => setViewDate((prev) => subMonths(prev, 1)),
setDate: setSelectedDate,
};
}
const MyCalendar = () => {
const engine = useCalendarEngine();
return <Calendar engine={engine} />;
};
// With Time Selection
const CalendarWithTime = () => {
const engine = useCalendarEngine();
return <Calendar engine={engine} granularity="minute" />;
};
// With Seconds
const CalendarWithSeconds = () => {
const engine = useCalendarEngine();
return <Calendar engine={engine} granularity="second" />;
};Props:
engine: TheCalendarEngineobject containing state and methods (required).weeks: A 2D array ofCalendarEngineDayobjects representing the calendar grid.viewDate: The currently displayed month/year.goToNextMonth(): Navigate to the next month.goToPrevMonth(): Navigate to the previous month.setDate(date: Date): Set the selected date.
granularity:date(default),minute,second- Controls whether to show time selection.isDisabled: Disables all interactions.isReadOnly: Prevents date selection but allows navigation.classNames: Custom styles for component slots:base,header,headerTitle,grid,weekdaysWrapper,daysWrapperweekday,cell,cellSelected,cellToday,cellDisabled,cellOutside,time
Features:
- Headless Architecture: Bring your own state management via the
engineprop. - Full Keyboard Navigation: Arrow keys, Home/End, PageUp/PageDown support.
- Accessibility: ARIA grid pattern with proper roles and labels.
- Time Selection: Optional time picker for minute or second granularity.
- Helper Utility: Use
getMonthGrid()to generate calendar data from state.
Select
A form component for selecting a value from a set of options, supporting single and multiple selection.
import { Select, SelectItem } from "rupoui";
// Basic
<Select label="Favorite Animal">
<SelectItem key="cat">Cat</SelectItem>
<SelectItem key="dog">Dog</SelectItem>
</Select>
// Multiple Selection
<Select
label="Tech Stack"
selectionMode="multiple"
>
<SelectItem key="react">React</SelectItem>
<SelectItem key="vue">Vue</SelectItem>
</Select>Features:
- Dynamic: Supports single and multiple selection modes.
- Controlled: Fully controllable via
valueandonValueChange. - Accessible: Keyboard navigation and ARIA support out of the box.
- Customizable: Styling slots for
trigger,popover,listbox,option, etc.
Checkbox
A control that allows the user to select one or more items from a set.
import { Checkbox, CheckboxGroup } from "rupoui";
// Single
<Checkbox>Accept terms</Checkbox>
// Indeterminate
<Checkbox isIndeterminate>Select all</Checkbox>
// Group
<CheckboxGroup
label="Select cities"
defaultValue={["sydney", "london"]}
color="warning"
>
<Checkbox value="sydney">Sydney</Checkbox>
<Checkbox value="london">London</Checkbox>
<Checkbox value="tokyo">Tokyo</Checkbox>
</CheckboxGroup>Features:
- Controlled/Uncontrolled: Supports both
isSelected/onValueChangeanddefaultSelected. - Indeterminate: Support for indeterminate state.
- Group Context:
CheckboxGroupmanages shared state and propagates styles (color,size, etc.) to children. - Accessibility: Focus rings and native keyboard support.
Radio & RadioGroup
A set of checkable buttons where only one can be checked at a time.
import { Radio, RadioGroup } from "rupoui";
<RadioGroup label="Select Plan" defaultValue="free" color="secondary">
<Radio value="free">Free</Radio>
<Radio value="pro">Pro (Recommended)</Radio>
<Radio value="enterprise" description="Contact sales">
Enterprise
</Radio>
</RadioGroup>;Features:
- Single Selection: Managed automatically by
RadioGroup. - Customizable: Full support for variants, colors, and sizes.
- Accessibility: Full ARIA support and keyboard navigation.
Textarea
A multiline text input component with consistent styling and resizing controls.
import { Textarea } from "rupoui";
<Textarea label="Description" placeholder="Enter your description" />
<Textarea
label="Bio"
variant="bordered"
resize="vertical"
rows={4}
/>Props:
label,placeholder,description,errorMessage.resize:none(default),vertical,horizontal,both.rows: Number of text rows.- Standard
Inputstyling props (variant,color,size,radius).
Spinner
loading indicator.
import { Spinner } from "rupoui";
<Spinner size="lg" color="primary" />;Switch
A control that allows the user to toggle between checked and not checked.
import { Switch } from "rupoui";
<Switch defaultSelected>WiFi</Switch>
<Switch color="success" size="lg" startContent={<SunIcon />} endContent={<MoonIcon />}>
Dark Mode
</Switch>Props:
size:sm,md(default),lg.color:primary(default),secondary,success,warning,danger.startContent,endContent: Icons or text inside the thumb.isSelected,defaultSelected,onValueChange.
Dropdown
A floating menu that displays a list of actions or options.
import { Dropdown, DropdownContent, DropdownItem, DropdownTrigger } from "rupoui";
<Dropdown>
<DropdownTrigger>
<Button variant="bordered">Open Menu</Button>
</DropdownTrigger>
<DropdownContent>
<DropdownItem key="new">New file</DropdownItem>
<DropdownItem key="copy">Copy link</DropdownItem>
<DropdownItem key="edit">Edit file</DropdownItem>
<DropdownItem key="delete" className="text-danger" color="danger">
Delete file
</DropdownItem>
</DropdownContent>
</Dropdown>;Features:
- Trigger: Can wrap any element (Button, Avatar, etc.).
- Content: Supports sections, dividers, and custom styling.
- Interaction: Handles focus, keyboard navigation, and click outside automatically.
Modal
A dialog that focuses the user's attention exclusively on an information or an action.
import {
Button,
Modal,
ModalBody,
ModalContent,
ModalFooter,
ModalHeader,
ModalTrigger,
} from "rupoui";
<Modal>
<ModalTrigger>
<Button>Open Modal</Button>
</ModalTrigger>
<ModalContent>
{(onClose) => (
<>
<ModalHeader>Modal Title</ModalHeader>
<ModalBody>
<p>Modal content goes here...</p>
</ModalBody>
<ModalFooter>
<Button color="danger" variant="light" onPress={onClose}>
Close
</Button>
<Button color="primary" onPress={onClose}>
Action
</Button>
</ModalFooter>
</>
)}
</ModalContent>
</Modal>;Props:
size:xsto5xl,full.backdrop:opaque(default),blur,transparent.placement:auto,top,center(default),bottom.scrollBehavior:normal(default),inside,outside.classNames: Custom styles for component slots:base,backdrop,wrapper,header,body,footer,closeButton.
Tooltip
A popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it.
import { Button, Tooltip } from "rupoui";
<Tooltip content="I am a tooltip">
<Button>Hover me</Button>
</Tooltip>;Props:
content: The content of the tooltip.placement: Position relative to the trigger (top, bottom, left, right, etc.).delay: Delay in milliseconds before opening.closeDelay: Delay in milliseconds before closing.offset: Distance from the trigger.
Popover
A non-modal dialog that floats around a trigger. It is used to display rich content behind a trigger element.
import { Button, Popover, PopoverContent, PopoverTrigger } from "rupoui";
<Popover placement="bottom">
<PopoverTrigger>
<Button>Open Popover</Button>
</PopoverTrigger>
<PopoverContent showArrow={true}>
<div className="px-1 py-2">
<div className="text-small font-bold">Popover Content</div>
<div className="text-tiny">This is the popover content</div>
</div>
</PopoverContent>
</Popover>;Props:
placement: Position relative to the trigger (top,bottom,left,right, etc.).offset: Distance from the trigger.trigger: Interaction to trigger the popover:click(default),hover.isOpen/defaultOpen/onOpenChange: Controlled and uncontrolled state management.showArrow: Whether to show an arrow (onPopoverContent).motionPreset:scale(default),fade,slide(onPopoverContent).
Drawer
A panel that slides out from the edge of the screen, commonly used for navigation, details, or settings.
import {
Button,
Drawer,
DrawerBody,
DrawerContent,
DrawerFooter,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
} from "rupoui";
<Drawer side="right" backdrop="blur">
<DrawerTrigger>
<Button>Open Drawer</Button>
</DrawerTrigger>
<DrawerContent>
<DrawerHeader>
<DrawerTitle>Edit Profile</DrawerTitle>
</DrawerHeader>
<DrawerBody>
<p>Make changes to your profile here.</p>
</DrawerBody>
<DrawerFooter>
<Button variant="light">Cancel</Button>
<Button color="primary">Save</Button>
</DrawerFooter>
</DrawerContent>
</Drawer>;Props:
open/defaultOpen/onOpenChange: Controlled and uncontrolled state management.side:left,right(default),top,bottom.radius:none,sm,md,lg(default),xl.backdrop:opaque(default),blur,transparent.closeOnOverlayClick: Whether clicking the overlay closes the drawer (default:true).closeOnEscape: Whether pressing Escape closes the drawer (default:true).blockScrollOnOpen: Whether to lock body scroll when open (default:true).portal: Whether to render inside a portal (default:true).classNames: Custom styles for component slots:base,overlay,content,header,title,body,footer.
Tabs
A component that allows users to switch between different views or functional aspects of an application.
import { Tabs, Tab } from "rupoui";
<Tabs aria-label="Options">
<Tab key="photos" title="Photos">
<Card>Photos Content</Card>
</Tab>
<Tab key="music" title="Music">
<Card>Music Content</Card>
</Tab>
<Tab key="videos" title="Videos">
<Card>Videos Content</Card>
</Tab>
</Tabs>
// Vertical
<Tabs isVertical aria-label="Vertical tabs">
<Tab key="settings" title="Settings" />
<Tab key="permissions" title="Permissions" />
</Tabs>Props:
variant:solid(default),underlined,bordered,light.color:default(default),primary,secondary,success,warning,danger.size:sm,md(default),lg.radius:none,sm,md,lg,full.isVertical: Whether to render tabs vertically.fullWidth: Whether to take up the full width of the container.
Pagination
A navigation component that allows users to navigate through pages of content with smooth animations.
import { Pagination } from "rupoui";
// One-Liner API (Recommended)
<Pagination
page={currentPage}
totalPages={10}
onPageChange={setCurrentPage}
/>
// With variant and size
<Pagination
page={currentPage}
totalPages={20}
onPageChange={setCurrentPage}
variant="outline"
size="lg"
/>
// Uncontrolled mode
<Pagination
defaultPage={5}
totalPages={10}
onPageChange={(page) => console.log("Page:", page)}
variant="ghost"
/>
// Hide controls
<Pagination
defaultPage={3}
totalPages={10}
showPrevious={false}
showNext={false}
/>Advanced Composition:
For full control over the pagination layout, use the composable API:
import {
Pagination,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationNext,
PaginationPrevious,
usePagination,
} from "rupoui";
const MyPagination = () => {
const [page, setPage] = useState(1);
const range = usePagination({
totalPages: 20,
currentPage: page,
siblingCount: 2,
boundaryCount: 1,
});
return (
<Pagination page={page} totalPages={20} onPageChange={setPage}>
<PaginationContent>
<PaginationPrevious />
{range.map((item, index) =>
item === "ellipsis" ? (
<PaginationEllipsis key={`ellipsis-${index}`} />
) : (
<PaginationItem key={item} value={item} />
),
)}
<PaginationNext />
</PaginationContent>
</Pagination>
);
};Props:
page/defaultPage: Current page number (controlled/uncontrolled).totalPages: Total number of pages (required).onPageChange: Callback when the page changes.variant:solid(default),outline,ghost.size:sm,md(default),lg.siblingCount: Number of pages to show on each side of current page (default:1).boundaryCount: Number of pages to show at start and end (default:1).showPrevious/showNext: Whether to show navigation buttons (default:true).disabled: Whether pagination is disabled.classNames: Custom styles for component slots (base,content,item,prev,next,ellipsis).
Features:
- One-Liner API: Simple and intuitive for common use cases.
- Composable: Full control with advanced composition when needed.
- Animated: Smooth transitions using Framer Motion's
layoutId. - Accessible: Full keyboard navigation and ARIA support.
- Customizable: Multiple variants, sizes, and styling options.
Chip
A compact element that represents an input, attribute, or action.
import { Chip, ChipGroup } from "rupoui";
// Basic
<Chip>Default</Chip>
// With Variants and Colors
<Chip variant="bordered" color="primary">Primary</Chip>
<Chip variant="flat" color="warning">Warning</Chip>
// Interactive & Closeable
<Chip onClose={() => console.log('closed')} isCloseable>
Closeable
</Chip>
// Chip Group (Multiple Selection)
<ChipGroup
selectionMode="multiple"
color="secondary"
defaultValue={["react"]}
>
<Chip value="react">React</Chip>
<Chip value="vue">Vue</Chip>
<Chip value="angular">Angular</Chip>
</ChipGroup>Props:
variant:solid(default),bordered,flat,light.color:default(default),primary,secondary,success,warning,danger.size:sm,md(default),lg.radius:sm,md,lg,xl,full(default).onClose: Handler when the close button is clicked.isCloseable: Whether to show the close button.isSelected: Controlled selection state.startContent/endContent: Icons or content padding.
Features:
- Interactive: Supports selection, dismissal, and click interactions.
- Groupable:
ChipGroupmanages selection state and styles for children.
Toast
A succinct message that is displayed temporarily.
import { Toaster, toast } from "rupoui";
// 1. Add Toaster to your app root
<Toaster position="bottom-right" />
// 2. Trigger toasts
<Button onClick={() => toast("Default toast")}>Default</Button>
<Button onClick={() => toast.success("Success event")}>Success</Button>
<Button onClick={() => toast.error("Error event")}>Error</Button>
<Button onClick={() => toast.loading("Loading...")}>Loading</Button>Toaster Props:
position: Position of the toast notifications.top-left,top-right,top-centerbottom-left,bottom-right,bottom-center
expand: Whether to expand the toast list on hover (default:false).richColors: Whether to use rich colors for variants (default:false).
Toast API:
toast(message, options): Default toast.toast.success(message, options): Success variant.toast.error(message, options): Error variant.toast.warning(message, options): Warning variant.toast.info(message, options): Info variant.toast.loading(message, options): Loading variant.toast.dismiss(id): Dismiss a specific toast.
Features:
- Queue System: Automatically manages multiple toasts.
- Variants: Built-in styles for success, error, warning, and info.
- Promise Support: Handle async operations with loading states.
- Customizable: Full control over styling and positioning.
🎨 Customization
Global Configuration
Set default props for any component globally via the provider:
<RupoUIProvider
config={{
Button: {
radius: "full",
variant: "bordered",
},
Input: {
size: "lg",
},
Textarea: {
variant: "faded",
},
Checkbox: {
color: "success",
},
// ... other supported components
}}
>
<App />
</RupoUIProvider>Granular Styling
Use the classNames prop to override inner slots of any component:
<Input
classNames={{
input: "text-lg font-bold",
inputWrapper: "bg-gray-100 dark:bg-gray-800",
label: "text-primary",
}}
/>📄 License
MIT
