The @repo/ui package provides a comprehensive React component library for building consistent user interfaces across the Exness Trading Platform.
Installation
This package is internal to the monorepo and installed automatically:
"dependencies" : {
"@repo/ui" : "workspace:*"
}
Features
50+ Components : Comprehensive UI component library
Radix UI : Built on unstyled, accessible components
Tailwind CSS : Utility-first styling with CSS variables
Dark Mode : Built-in theme support with next-themes
TypeScript : Full type safety and IntelliSense
Accessible : WCAG compliant components
Customizable : Style variants using class-variance-authority
Package Structure
The package exports components, utilities, and hooks through subpath exports:
import { Button } from '@repo/ui/components/button' ;
import { Card } from '@repo/ui/components/card' ;
import { cn } from '@repo/ui/lib/utils' ;
import { useIsMobile } from '@repo/ui/hooks/use-mobile' ;
import '@repo/ui/globals.css' ;
Available Components
Accordion Collapsible content panels
Alert Status messages and notifications
Alert Dialog Modal confirmation dialogs
Avatar User profile images
Badge Status indicators and labels
Breadcrumb Navigation breadcrumbs
Button Interactive buttons
Button Group Grouped button controls
Calendar Date picker component
Carousel Image/content carousel
Collapsible Expandable content
Context Menu Right-click menus
Dropdown Menu Dropdown menus
Empty Empty state displays
Hover Card Hoverable popovers
Input Group Grouped inputs
Input OTP OTP input component
And 25+ more components including Labels, Menus, Modals, Popovers, Progress bars, Select dropdowns, Sliders, Tables, Tabs, Textareas, Toasts, Tooltips, and more!
Basic Usage
import { Button } from '@repo/ui/components/button' ;
export function ButtonDemo () {
return (
< div className = "flex gap-2" >
< Button variant = "default" > Default </ Button >
< Button variant = "destructive" > Destructive </ Button >
< Button variant = "outline" > Outline </ Button >
< Button variant = "secondary" > Secondary </ Button >
< Button variant = "ghost" > Ghost </ Button >
< Button variant = "link" > Link </ Button >
</ div >
);
}
Card Component
import {
Card ,
CardHeader ,
CardTitle ,
CardDescription ,
CardContent ,
CardFooter ,
CardAction
} from '@repo/ui/components/card' ;
import { Button } from '@repo/ui/components/button' ;
export function TradingCard () {
return (
< Card >
< CardHeader >
< CardTitle > BTC / USDT </ CardTitle >
< CardDescription > Bitcoin to US Dollar </ CardDescription >
< CardAction >
< Button size = "sm" variant = "outline" > Trade </ Button >
</ CardAction >
</ CardHeader >
< CardContent >
< div className = "text-3xl font-bold" > $50 , 000 . 00 </ div >
< div className = "text-sm text-green-600" >+ 2.5 % (24 h ) </ div >
</ CardContent >
< CardFooter >
< Button className = "w-full" > Place Order </ Button >
</ CardFooter >
</ Card >
);
}
Order Entry Form
import { Card , CardHeader , CardTitle , CardContent } from '@repo/ui/components/card' ;
import { Button } from '@repo/ui/components/button' ;
import { Input } from '@repo/ui/components/input' ;
import { Label } from '@repo/ui/components/label' ;
import { Select } from '@repo/ui/components/select' ;
export function OrderForm () {
return (
< Card >
< CardHeader >
< CardTitle > Place Order </ CardTitle >
</ CardHeader >
< CardContent className = "space-y-4" >
< div className = "space-y-2" >
< Label htmlFor = "symbol" > Trading Pair </ Label >
< Select >
< option value = "btc" > BTC / USDT </ option >
< option value = "eth" > ETH / USDT </ option >
< option value = "sol" > SOL / USDT </ option >
</ Select >
</ div >
< div className = "space-y-2" >
< Label htmlFor = "quantity" > Quantity </ Label >
< Input
id = "quantity"
type = "number"
placeholder = "0.00"
/>
</ div >
< div className = "space-y-2" >
< Label htmlFor = "price" > Price ( USDT ) </ Label >
< Input
id = "price"
type = "number"
placeholder = "0.00"
/>
</ div >
< div className = "grid grid-cols-2 gap-2" >
< Button variant = "default" className = "w-full" >
Buy
</ Button >
< Button variant = "destructive" className = "w-full" >
Sell
</ Button >
</ div >
</ CardContent >
</ Card >
);
}
Price Ticker
import { Badge } from '@repo/ui/components/badge' ;
import { Card , CardContent } from '@repo/ui/components/card' ;
import { ArrowUp , ArrowDown } from 'lucide-react' ;
interface PriceTickerProps {
symbol : string ;
price : number ;
change : number ;
}
export function PriceTicker ({ symbol , price , change } : PriceTickerProps ) {
const isPositive = change >= 0 ;
return (
< Card >
< CardContent className = "flex items-center justify-between p-4" >
< div >
< div className = "text-sm text-muted-foreground" > { symbol } </ div >
< div className = "text-2xl font-bold" > $ {price.toFixed( 2 ) } </ div >
</ div >
< Badge
variant = {isPositive ? "default" : "destructive" }
className = "flex items-center gap-1"
>
{ isPositive ? < ArrowUp className = "size-3" /> : <ArrowDown className="size-3" / > }
{ Math . abs ( change ). toFixed (2)} %
</ Badge >
</ CardContent >
</ Card >
);
}
Portfolio Overview
import { Card , CardHeader , CardTitle , CardContent } from '@repo/ui/components/card' ;
import { Progress } from '@repo/ui/components/progress' ;
interface Asset {
symbol : string ;
percentage : number ;
value : number ;
}
export function PortfolioOverview ({ assets } : { assets : Asset [] }) {
const total = assets . reduce (( sum , asset ) => sum + asset . value , 0 );
return (
< Card >
< CardHeader >
< CardTitle > Portfolio Allocation </ CardTitle >
</ CardHeader >
< CardContent className = "space-y-4" >
{ assets . map (( asset ) => (
< div key = {asset. symbol } className = "space-y-2" >
< div className = "flex justify-between text-sm" >
< span >{asset. symbol } </ span >
< span className = "font-medium" >
$ {asset.value.toFixed( 2 ) } ({asset.percentage.toFixed( 1 ) } % )
</ span >
</ div >
< Progress value = {asset. percentage } />
</ div >
))}
< div className = "pt-4 border-t" >
< div className = "flex justify-between font-bold" >
< span > Total </ span >
< span > $ {total.toFixed( 2 ) } </ span >
</ div >
</ div >
</ CardContent >
</ Card >
);
}
Utilities
cn() - Class Name Utility
Combines class names with Tailwind merge:
import { cn } from '@repo/ui/lib/utils' ;
export function Component ({ className } : { className ?: string }) {
return (
< div
className = { cn (
'rounded-lg border p-4' , // Base classes
className // Override classes
)}
>
Content
</ div >
);
}
// Usage
< Component className = "bg-blue-500 text-white" />
Hooks
useIsMobile
Detects if the viewport is mobile-sized:
import { useIsMobile } from '@repo/ui/hooks/use-mobile' ;
import { Button } from '@repo/ui/components/button' ;
import { Drawer } from '@repo/ui/components/drawer' ;
import { Dialog } from '@repo/ui/components/dialog' ;
export function ResponsiveModal ({ children } : { children : React . ReactNode }) {
const isMobile = useIsMobile ();
if ( isMobile ) {
return < Drawer >{ children } </ Drawer > ;
}
return < Dialog >{ children } </ Dialog > ;
}
Styling
Import Global Styles
Import the global styles in your root layout:
// app/layout.tsx
import '@repo/ui/globals.css' ;
export default function RootLayout ({ children }) {
return (
< html lang = "en" >
< body >{ children } </ body >
</ html >
);
}
Theme Configuration
The package uses CSS variables for theming:
/* Customize in your app's globals.css */
:root {
--background : 0 0 % 100 % ;
--foreground : 222.2 84 % 4.9 % ;
--primary : 222.2 47.4 % 11.2 % ;
--primary-foreground : 210 40 % 98 % ;
/* ... more variables */
}
.dark {
--background : 222.2 84 % 4.9 % ;
--foreground : 210 40 % 98 % ;
/* ... dark mode variables */
}
Dark Mode
import { ThemeProvider } from 'next-themes' ;
export function Providers ({ children } : { children : React . ReactNode }) {
return (
< ThemeProvider attribute = "class" defaultTheme = "system" >
{ children }
</ ThemeProvider >
);
}
Component Variants
Many components use class-variance-authority for variants:
import { Button , buttonVariants } from '@repo/ui/components/button' ;
import Link from 'next/link' ;
// Use as component
< Button variant = "outline" size = "lg" > Click me </ Button >
// Use variants with other elements
< Link
href = "/features/real-time-trading"
className = { buttonVariants ({ variant : "ghost" })}
>
Trade
</ Link >
Best Practices
Import components individually
Import only the components you need to optimize bundle size: // Good
import { Button } from '@repo/ui/components/button' ;
import { Card } from '@repo/ui/components/card' ;
// Avoid
import * from '@repo/ui' ;
Always use the cn() utility for combining class names: import { cn } from '@repo/ui/lib/utils' ;
< div className = { cn ( 'base-class' , conditionalClass && 'active' )} />
Leverage component composition
Build complex UIs by composing simple components: < Card >
< CardHeader >
< CardTitle > Title </ CardTitle >
</ CardHeader >
< CardContent > Content </ CardContent >
</ Card >
Combine components with hooks for responsive designs: const isMobile = useIsMobile ();
return isMobile ? < MobileView /> : < DesktopView />;
Customization
Extending Components
import { Button } from '@repo/ui/components/button' ;
import { cn } from '@repo/ui/lib/utils' ;
export function TradingButton ({
children ,
className ,
... props
} : React . ComponentProps < typeof Button >) {
return (
< Button
className = { cn ( 'font-bold uppercase tracking-wide' , className )}
{ ... props }
>
{ children }
</ Button >
);
}
Creating Custom Variants
import { cva } from 'class-variance-authority' ;
import { cn } from '@repo/ui/lib/utils' ;
const tradingCardVariants = cva (
'rounded-lg border p-4' ,
{
variants: {
trend: {
up: 'border-green-500 bg-green-50' ,
down: 'border-red-500 bg-red-50' ,
neutral: 'border-gray-300'
}
}
}
);
export function TradingCard ({ trend , className , children }) {
return (
< div className = { cn ( tradingCardVariants ({ trend }), className)} >
{ children }
</ div >
);
}
@repo/utils Utility functions for use with UI components
@repo/types Shared type definitions for component props