The ProgressCard component is a reusable card for displaying learning metrics and statistics with consistent styling across the dashboard.
components/dashboard/progress-card.tsx
interface ProgressCardProps {
label: string;
value: string | number;
icon: LucideIcon;
trend?: 'up' | 'down' | 'neutral';
trendValue?: string;
colorScheme?: 'blue' | 'green' | 'purple' | 'orange' | 'primary';
className?: string;
}- Type:
string - Description: The descriptive label for the metric (e.g., "Study Time", "Completion Rate")
- Example:
"Daily Streak"
- Type:
string | number - Description: The main value to display (e.g., "12h", "85%", 42)
- Example:
"12h"or85
- Type:
LucideIcon - Description: A Lucide React icon component to display in the card
- Available icons: Clock, Target, TrendingUp, Award, BookOpen, etc.
- Example:
Clock(imported from 'lucide-react')
- Type:
'up' | 'down' | 'neutral' - Description: Indicates the trend direction (currently not visually rendered but available for future use)
- Default:
undefined - Example:
"up"
- Type:
string - Description: A badge showing change or trend (e.g., "+2h", "-5%", "↑15%")
- Default:
undefined - Example:
"+2h"
- Type:
'blue' | 'green' | 'purple' | 'orange' | 'primary' - Description: The color scheme for the card's icon and trend badge
- Default:
'primary' - Example:
"blue"
- Type:
string - Description: Additional CSS classes to apply to the card
- Default:
undefined - Example:
"col-span-2"
Each color scheme provides a cohesive set of colors for the icon background, icon color, and trend badge:
{
bg: 'bg-blue-50',
text: 'text-blue-600',
iconBg: 'bg-blue-100',
trendBg: 'bg-blue-50',
trendText: 'text-blue-700',
}Use for: Time-related metrics, study duration, session counts
{
bg: 'bg-green-50',
text: 'text-green-600',
iconBg: 'bg-green-100',
trendBg: 'bg-green-50',
trendText: 'text-green-700',
}Use for: Completion rates, success metrics, achievements
{
bg: 'bg-purple-50',
text: 'text-purple-600',
iconBg: 'bg-purple-100',
trendBg: 'bg-purple-50',
trendText: 'text-purple-700',
}Use for: Performance metrics, scores, grades
{
bg: 'bg-orange-50',
text: 'text-orange-600',
iconBg: 'bg-orange-100',
trendBg: 'bg-orange-50',
trendText: 'text-orange-700',
}Use for: Streaks, consistency metrics, engagement
{
bg: 'bg-primary-50',
text: 'text-primary-600',
iconBg: 'bg-primary-100',
trendBg: 'bg-primary-50',
trendText: 'text-primary-700',
}Use for: Brand-specific metrics, general purpose
import { Clock } from 'lucide-react';
import { ProgressCard } from '@/components/dashboard/progress-card';
<ProgressCard
label="Study Time"
value="12h"
icon={Clock}
colorScheme="blue"
/>import { Target } from 'lucide-react';
import { ProgressCard } from '@/components/dashboard/progress-card';
<ProgressCard
label="Completion Rate"
value="85%"
icon={Target}
colorScheme="green"
trendValue="+5%"
/>import { Award } from 'lucide-react';
import { ProgressCard } from '@/components/dashboard/progress-card';
<ProgressCard
label="Daily Streak"
value="15 days"
icon={Award}
colorScheme="orange"
className="col-span-2"
/>import { Clock, Target, TrendingUp, Award } from 'lucide-react';
import { ProgressCard } from '@/components/dashboard/progress-card';
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
<ProgressCard
label="Study Time"
value="12h"
icon={Clock}
colorScheme="blue"
trendValue="+2h"
/>
<ProgressCard
label="Completion Rate"
value="85%"
icon={Target}
colorScheme="green"
trendValue="+5%"
/>
<ProgressCard
label="Average Score"
value="92%"
icon={TrendingUp}
colorScheme="purple"
trendValue="+3%"
/>
<ProgressCard
label="Daily Streak"
value="15 days"
icon={Award}
colorScheme="orange"
/>
</div><div className="rounded-xl bg-white border border-gray-200 p-5 hover:shadow-md">
{/* Icon and trend badge row */}
<div className="flex items-start justify-between mb-4">
{/* Icon with colored background */}
<div className="rounded-lg bg-{color}-100 p-2.5">
<Icon className="h-5 w-5 text-{color}-600" />
</div>
{/* Optional trend badge */}
{trendValue && (
<span className="text-xs font-semibold px-2.5 py-1 rounded-full bg-{color}-50 text-{color}-700">
{trendValue}
</span>
)}
</div>
{/* Value and label */}
<div>
<p className="text-2xl font-bold text-gray-900 mb-1">{value}</p>
<p className="text-sm font-medium text-gray-600">{label}</p>
</div>
</div>- Border radius:
rounded-xl(12px) - Background: White (
bg-white) - Border: Subtle gray (
border border-gray-200) - Padding:
p-5(20px) - Hover effect: Shadow increase (
hover:shadow-md) - Transition: Smooth (
transition-allvia cn utility)
- Border radius:
rounded-lg(8px) - Padding:
p-2.5(10px) - Background: Color-specific light background
- Icon size:
h-5 w-5(20px) - Icon color: Color-specific dark shade
- Font size:
text-2xl(24px) - Font weight:
font-bold - Color:
text-gray-900 - Margin bottom:
mb-1(4px)
- Font size:
text-sm(14px) - Font weight:
font-medium - Color:
text-gray-600
- Font size:
text-xs(12px) - Font weight:
font-semibold - Padding:
px-2.5 py-1(10px horizontal, 4px vertical) - Border radius:
rounded-full - Background: Color-specific light background
- Text color: Color-specific darker shade
- Uses semantic
divelements with proper structure - Clear visual hierarchy
- All color combinations meet WCAG AA standards
- Text remains readable on all backgrounds
- Icon colors provide sufficient contrast
- Card is not inherently interactive
- If wrapped in a button/link, ensure proper focus states
The ProgressCard component is inherently responsive through:
- Flexible width: Adapts to container size
- Consistent padding: Maintains proper spacing at all sizes
- Readable text: Font sizes scale appropriately
- Grid compatibility: Works in any grid system
Mobile (< 640px):
<div className="grid grid-cols-1 gap-4">
{/* Single column */}
</div>Tablet (640px - 1024px):
<div className="grid grid-cols-2 gap-4">
{/* Two columns */}
</div>Desktop (> 1024px):
<div className="grid grid-cols-4 gap-4">
{/* Four columns */}
</div>Responsive:
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
{/* Adapts based on screen size */}
</div>- Use descriptive labels that clearly indicate what the metric represents
- Choose appropriate color schemes that match the metric type
- Keep values concise and easy to scan (use abbreviations like "h" for hours)
- Use trend values to show change over time when available
- Maintain consistent icon sizes and styles
- Group related metrics together
- Use extremely long labels that wrap to multiple lines
- Mix different units without clear indication (e.g., "12" without "h" or "hours")
- Overuse trend badges when data isn't available
- Use color schemes inconsistently across similar metrics
- Add interactive elements without proper accessibility support
- Create cards with empty or placeholder content
<ProgressCard label="Study Time" value="12h" icon={Clock} colorScheme="blue" />
<ProgressCard label="Lessons Completed" value="24" icon={BookOpen} colorScheme="green" />
<ProgressCard label="Average Score" value="85%" icon={TrendingUp} colorScheme="purple" />
<ProgressCard label="Current Streak" value="7 days" icon={Award} colorScheme="orange" /><ProgressCard label="Enrolled Students" value="1,234" icon={Users} colorScheme="primary" />
<ProgressCard label="Completion Rate" value="78%" icon={Target} colorScheme="green" />
<ProgressCard label="Average Rating" value="4.8" icon={Star} colorScheme="orange" />
<ProgressCard label="Total Hours" value="5,678h" icon={Clock} colorScheme="blue" /><ProgressCard label="Quiz Average" value="92%" icon={TrendingUp} colorScheme="purple" trendValue="+3%" />
<ProgressCard label="Practice Time" value="8h" icon={Clock} colorScheme="blue" trendValue="+1h" />
<ProgressCard label="Mastery Level" value="Advanced" icon={Award} colorScheme="green" />import { ProgressCard } from '@/components/dashboard/progress-card';
const stats = [
{ label: 'Study Time', value: `${studyTime}h`, icon: Clock, colorScheme: 'blue' as const },
{ label: 'Completion Rate', value: `${completionRate}%`, icon: Target, colorScheme: 'green' as const },
{ label: 'Average Score', value: `${avgScore}%`, icon: TrendingUp, colorScheme: 'purple' as const },
{ label: 'Daily Streak', value: `${streak} days`, icon: Award, colorScheme: 'orange' as const },
];
return (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
{stats.map((stat) => (
<ProgressCard key={stat.label} {...stat} />
))}
</div>
);- Click handler: Make cards interactive with onClick prop
- Loading state: Built-in skeleton loader
- Animated values: Count-up animation for numbers
- Comparison mode: Show previous period comparison
- Tooltip: Detailed information on hover
- Size variants: sm, md, lg options
- Dark mode: Dark theme support
- Custom icons: Allow custom icon components
<ProgressCard
label="Study Time"
value={12}
previousValue={10}
icon={Clock}
colorScheme="blue"
size="lg"
animated
onClick={() => handleCardClick()}
tooltip="Total study time this week"
/>import { render, screen } from '@testing-library/react';
import { Clock } from 'lucide-react';
import { ProgressCard } from './progress-card';
describe('ProgressCard', () => {
it('renders with required props', () => {
render(
<ProgressCard
label="Study Time"
value="12h"
icon={Clock}
colorScheme="blue"
/>
);
expect(screen.getByText('Study Time')).toBeInTheDocument();
expect(screen.getByText('12h')).toBeInTheDocument();
});
it('renders trend value when provided', () => {
render(
<ProgressCard
label="Study Time"
value="12h"
icon={Clock}
colorScheme="blue"
trendValue="+2h"
/>
);
expect(screen.getByText('+2h')).toBeInTheDocument();
});
});- Initial release
- Five color schemes: blue, green, purple, orange, primary
- Optional trend value badge
- Hover effects
- Fully responsive
This documentation provides everything needed to use and extend the ProgressCard component effectively.