Skip to main content

Component Library

OneLibro's component library provides reusable, accessible UI components built with React, TypeScript, and Tailwind CSS v4.

Design Principles

1. Consistency

All components follow the same design patterns:

  • Color scheme (dark theme primary)
  • Typography scale
  • Spacing system (Tailwind spacing)
  • Border radius (rounded-lg for most components)
  • Transition timing (150ms for hovers, 300ms for state changes)

2. Accessibility

Components are built with accessibility in mind:

  • Semantic HTML elements
  • ARIA labels where needed
  • Keyboard navigation support
  • Focus indicators
  • Screen reader friendly

3. Responsive Design

Mobile-first approach:

  • Stack vertically on mobile
  • Use flexbox/grid for layouts
  • Touch-friendly tap targets (min 44x44px)
  • Responsive typography

4. Performance

Optimized for speed:

  • Lazy loading for heavy components
  • Memoization where appropriate
  • Minimal bundle size
  • CSS-in-JS avoided (Tailwind only)

Component Categories

Layout Components

Structural components for page organization:

  • DashboardLayout - Main layout with sidebar and top bar
  • DashboardCard - Reusable card container for dashboard widgets
  • Sidebar - Navigation sidebar with collapsible sections
  • TopBar - Header with user menu and notifications
  • BottomNav - Mobile navigation bar

Form Components

User input components:

  • TransactionForm - Add/edit transaction form
  • BudgetForm - Create/edit budget form
  • RequestInviteModal - Invite request modal dialog

Data Display Components

Components for showing data:

  • RecentTransactions - Transaction list with formatting
  • AccountCard - Bank account summary card
  • SpendingChart - Budget spending visualization (Recharts)

Loading States

Skeleton loaders for better perceived performance:

  • Skeleton - Base skeleton loader
  • DashboardSkeleton - Dashboard loading state
  • AccountCardSkeleton - Account card loading state
  • TransactionListSkeleton - Transaction list loading state

Integration Components

Third-party integrations:

  • PlaidLink - Plaid Link button/modal for bank connections
  • ErrorBoundary - Catch and display React errors gracefully

Authentication Components

Auth-related components:

  • ProtectedRoute - HOC for authenticated routes
  • FinanceLayoutClient - Finance app layout with auth context

Component Documentation

DashboardLayout

Main layout for authenticated finance app pages with sidebar navigation.

Location: components/finance/DashboardLayout.tsx

Props:

interface DashboardLayoutProps {
children: React.ReactNode;
title?: string; // Page title
showBackButton?: boolean; // Show back navigation
}

Usage:

import { DashboardLayout } from '@/components/finance/DashboardLayout';

export default function BudgetsPage() {
return (
<DashboardLayout title="Budgets" showBackButton={false}>
<div className="space-y-6">
{/* Page content */}
</div>
</DashboardLayout>
);
}

Features:

  • Responsive sidebar (collapses on mobile)
  • Top bar with user profile
  • Bottom navigation for mobile
  • Page title rendering
  • Loading states

DashboardCard

Reusable card component for dashboard widgets.

Location: components/finance/DashboardCard.tsx

Props:

interface DashboardCardProps {
title: string;
value: string | number;
subtitle?: string;
icon?: React.ReactNode;
trend?: 'up' | 'down' | 'neutral';
trendValue?: string;
onClick?: () => void;
loading?: boolean;
}

Usage:

<DashboardCard
title="Total Balance"
value={formatCurrency(totalBalance)}
subtitle="Across all accounts"
icon={<DollarSign className="w-6 h-6" />}
trend="up"
trendValue="+5.2%"
/>

Features:

  • Icon support
  • Trend indicators (up/down arrows)
  • Loading state (skeleton)
  • Clickable cards (optional)
  • Responsive sizing

AccountCard

Displays bank account information with balance and actions.

Location: components/finance/AccountCard.tsx

Props:

interface AccountCardProps {
account: {
id: string;
name: string;
official_name?: string;
type: string;
subtype: string;
current_balance: number;
available_balance?: number;
institution_name: string;
};
onSync?: (accountId: string) => void;
onUnlink?: (accountId: string) => void;
}

Usage:

<AccountCard
account={account}
onSync={handleSync}
onUnlink={handleUnlink}
/>

Features:

  • Account type badges (Checking, Savings, Credit Card)
  • Balance display (current and available)
  • Institution logo (future enhancement)
  • Sync button (refresh transactions)
  • Unlink button (disconnect account)
  • Last updated timestamp

Wrapper component for Plaid Link integration.

Location: components/finance/PlaidLink.tsx

Props:

interface PlaidLinkProps {
userId: string;
onSuccess: (publicToken: string, metadata: any) => void;
onExit?: () => void;
mode?: 'create' | 'update';
itemId?: string; // Required for update mode
children?: React.ReactNode; // Custom button content
}

Usage:

import { PlaidLink } from '@/components/finance/PlaidLink';

<PlaidLink
userId={user.id}
onSuccess={handlePlaidSuccess}
onExit={() => console.log('User exited Plaid')}
>
<button className="btn-primary">
Connect Bank Account
</button>
</PlaidLink>

Features:

  • Create mode (new bank connection)
  • Update mode (re-authenticate existing connection)
  • Error handling
  • Loading states
  • Customizable button

TransactionForm

Form for adding/editing transactions.

Location: components/finance/TransactionForm.tsx

Props:

interface TransactionFormProps {
transaction?: Transaction; // For editing
onSubmit: (data: TransactionFormData) => Promise<void>;
onCancel: () => void;
}

Usage:

<TransactionForm
transaction={existingTransaction}
onSubmit={handleSubmit}
onCancel={() => router.back()}
/>

Fields:

  • Account (select)
  • Amount (number input with currency formatting)
  • Date (date picker)
  • Category (select)
  • Description (text input)
  • Notes (textarea, optional)

Validation:

  • Amount must be > 0
  • Date required
  • Category required
  • Description required

BudgetForm

Form for creating/editing budgets.

Location: components/finance/BudgetForm.tsx

Props:

interface BudgetFormProps {
budget?: Budget; // For editing
onSubmit: (data: BudgetFormData) => Promise<void>;
onCancel: () => void;
}

Usage:

<BudgetForm
budget={existingBudget}
onSubmit={handleCreateBudget}
onCancel={() => router.push('/finance/budgets')}
/>

Fields:

  • Name (text input)
  • Category (select from predefined categories)
  • Limit (currency input)
  • Period (monthly/weekly)
  • Start date (date picker)

Validation:

  • Name required (min 3 chars)
  • Limit must be > 0
  • Period required
  • Category required

SpendingChart

Recharts-based visualization of budget spending.

Location: components/finance/SpendingChart.tsx

Props:

interface SpendingChartProps {
data: Array<{
category: string;
spent: number;
budget: number;
}>;
height?: number;
showLegend?: boolean;
}

Usage:

<SpendingChart
data={budgetData}
height={400}
showLegend={true}
/>

Features:

  • Bar chart comparing spent vs budget
  • Color coding (green = under budget, red = over budget)
  • Responsive sizing
  • Tooltip with formatted values
  • Legend (optional)

RecentTransactions

Transaction list component with formatting and actions.

Location: components/finance/RecentTransactions.tsx

Props:

interface RecentTransactionsProps {
transactions: Transaction[];
limit?: number; // Max transactions to show
showViewAll?: boolean; // Show "View All" link
onEdit?: (transaction: Transaction) => void;
onDelete?: (transactionId: string) => void;
}

Usage:

<RecentTransactions
transactions={recentTransactions}
limit={5}
showViewAll={true}
onEdit={handleEdit}
onDelete={handleDelete}
/>

Features:

  • Transaction rows with merchant name, category, amount
  • Date formatting (relative: "2 days ago")
  • Category badges with icons
  • Amount color coding (red for expenses, green for income)
  • Empty state message
  • Action buttons (edit, delete)

RequestInviteModal

Modal dialog for requesting an invite code.

Location: components/finance/RequestInviteModal.tsx

Props:

interface RequestInviteModalProps {
isOpen: boolean;
onClose: () => void;
}

Usage:

const [showModal, setShowModal] = useState(false);

<RequestInviteModal
isOpen={showModal}
onClose={() => setShowModal(false)}
/>

Features:

  • Email and name input fields
  • Form validation
  • Loading state during submission
  • Success state with auto-close
  • Error handling with user-friendly messages
  • Closes on overlay click or X button

Skeleton Loaders

Loading state components for better perceived performance.

Locations: components/finance/skeletons/

Available Skeletons:

  1. Skeleton - Base skeleton component

    <Skeleton className="w-full h-8" />
  2. DashboardSkeleton - Dashboard page loading

    <DashboardSkeleton />
  3. AccountCardSkeleton - Account card loading

    <AccountCardSkeleton />
  4. TransactionListSkeleton - Transaction list loading

    <TransactionListSkeleton count={5} />

Usage:

import { DashboardSkeleton } from '@/components/finance/skeletons';

{loading ? <DashboardSkeleton /> : <DashboardContent />}

ProtectedRoute

Higher-order component for protecting routes with authentication.

Location: components/finance/ProtectedRoute.tsx

Usage:

import { ProtectedRoute } from '@/components/finance/ProtectedRoute';

export default function BudgetsPage() {
return (
<ProtectedRoute>
<DashboardLayout>
{/* Protected content */}
</DashboardLayout>
</ProtectedRoute>
);
}

Features:

  • Redirects to login if not authenticated
  • Shows loading state during auth check
  • Preserves redirect URL after login
  • Works with AuthContext

ErrorBoundary

React error boundary for graceful error handling.

Location: components/finance/ErrorBoundary.tsx

Usage:

import { ErrorBoundary } from '@/components/finance/ErrorBoundary';

export default function RootLayout({ children }) {
return (
<ErrorBoundary>
{children}
</ErrorBoundary>
);
}

Features:

  • Catches React rendering errors
  • Displays user-friendly error message
  • "Try Again" button to recover
  • Logs errors to console (production: send to error tracking)

Styling Guidelines

Color Palette

Background Colors:

  • bg-[#0a0a0a] - Main background
  • bg-[#0f0f0f] - Card background
  • bg-[#1a1a1a] - Elevated elements

Border Colors:

  • border-[#2a2a2a] - Default border
  • border-[#3a3a3a] - Hover border

Text Colors:

  • text-white - Primary text
  • text-gray-300 - Secondary text
  • text-gray-400 - Tertiary text/placeholders
  • text-gray-500 - Disabled text

Accent Colors:

  • text-[#10b981] / bg-[#10b981] - Primary (green)
  • text-[#ef4444] - Danger/error (red)
  • text-[#f59e0b] - Warning (orange)
  • text-[#3b82f6] - Info (blue)

Typography

Font Family: System font stack (default Next.js)

Font Sizes:

  • text-xs (12px) - Labels, captions
  • text-sm (14px) - Body text
  • text-base (16px) - Default
  • text-lg (18px) - Subheadings
  • text-xl (20px) - Card titles
  • text-2xl (24px) - Page titles
  • text-3xl (30px) - Large headings

Font Weights:

  • font-normal (400) - Body text
  • font-medium (500) - Labels
  • font-semibold (600) - Buttons, headings
  • font-bold (700) - Emphasis

Spacing

Use Tailwind's spacing scale:

  • gap-2 (0.5rem / 8px) - Tight spacing
  • gap-4 (1rem / 16px) - Default spacing
  • gap-6 (1.5rem / 24px) - Section spacing
  • gap-8 (2rem / 32px) - Large spacing

Border Radius

  • rounded (4px) - Small elements
  • rounded-md (6px) - Buttons, inputs
  • rounded-lg (8px) - Cards (default)
  • rounded-xl (12px) - Large cards
  • rounded-full - Circular (avatars, badges)

Shadows

Minimal shadows for depth:

  • shadow-sm - Subtle elevation
  • shadow-md - Card elevation
  • shadow-lg - Modal/popover elevation

Animation & Transitions

Hover Effects

transition-colors duration-150
hover:bg-[#1a1a1a]
hover:border-[#3a3a3a]

Loading States

animate-pulse  /* Skeleton loaders */
animate-spin /* Spinners */

Smooth Scrolling

scroll-smooth

Accessibility Best Practices

Keyboard Navigation

  • All interactive elements must be keyboard accessible
  • Use tabIndex appropriately
  • Provide visible focus indicators

ARIA Labels

<button aria-label="Delete transaction">
<TrashIcon />
</button>

Semantic HTML

Use proper HTML elements:

  • <button> for actions
  • <a> for navigation
  • <nav> for navigation sections
  • <main> for main content

Focus Management

// Trap focus in modals
<Dialog onClose={onClose} initialFocus={cancelButtonRef}>
{/* Modal content */}
</Dialog>

Performance Optimization

Code Splitting

Lazy load heavy components:

const SpendingChart = dynamic(
() => import('@/components/finance/SpendingChart'),
{ loading: () => <Skeleton className="h-[400px]" /> }
);

Memoization

Prevent unnecessary re-renders:

const MemoizedTransactionList = React.memo(RecentTransactions);

Image Optimization

Use Next.js Image component:

import Image from 'next/image';

<Image
src="/oneLibro-logo.png"
alt="OneLibro"
width={120}
height={40}
priority
/>

Testing Components

Unit Tests (Vitest)

import { render, screen } from '@testing-library/react';
import { DashboardCard } from './DashboardCard';

describe('DashboardCard', () => {
it('renders title and value', () => {
render(<DashboardCard title="Balance" value="$1,234.56" />);
expect(screen.getByText('Balance')).toBeInTheDocument();
expect(screen.getByText('$1,234.56')).toBeInTheDocument();
});
});

Integration Tests (Playwright)

test('user can add transaction', async ({ page }) => {
await page.goto('/finance/transactions/add');
await page.fill('input[name="amount"]', '50.00');
await page.selectOption('select[name="category"]', 'Groceries');
await page.click('button[type="submit"]');
await expect(page).toHaveURL('/finance/transactions');
});

Future Component Additions

  • Toast notifications (success, error, info)
  • Date range picker
  • Multi-select dropdown
  • Data table with sorting/filtering
  • Chart components (line, pie, donut)
  • File upload component
  • Avatar component with fallback
  • Progress bar component
  • Tabs component
  • Accordion component