Goals UI Guidelines
This document provides UI/UX guidelines and patterns for implementing goal-related interfaces in the MTD application.
Design Principles
Visual Hierarchy
- Goal name and progress are the primary visual elements
- Status indicators use consistent color coding
- Deadlines and assignees are secondary information
- Actions are contextually placed
Progressive Disclosure
- Show essential information in list views
- Reveal detailed metrics on hover/click
- Use expandable sections for advanced options
- Keep creation wizards step-based
Component Patterns
Goal Card Component
A standard goal card should include:
<Card sx={{ p: 2, cursor: 'pointer' }}>
<Box display="flex" justifyContent="space-between" alignItems="start">
<Box flex={1}>
<Typography variant="h6">{goal.name}</Typography>
<Typography variant="body2" color="text.secondary">
{goal.description}
</Typography>
</Box>
<GoalStatusChip status={goal.status} />
</Box>
<Box mt={2}>
<GoalProgressBar
value={goal.progressPercentage}
status={goal.status}
/>
<Typography variant="caption" sx={{ mt: 0.5 }}>
{goal.currentValue} / {goal.targetValue} {getUnitLabel(goal)}
</Typography>
</Box>
<Box display="flex" justifyContent="space-between" mt={2}>
<AvatarGroup assignees={goal.assignees} />
<Typography variant="caption" color="text.secondary">
{formatTimeRemaining(goal.endDate)}
</Typography>
</Box>
</Card>
Progress Visualization
Linear Progress Bar
<LinearProgress
variant="determinate"
value={progressPercentage}
sx={{
height: 8,
borderRadius: 4,
backgroundColor: 'grey.200',
'& .MuiLinearProgress-bar': {
borderRadius: 4,
backgroundColor: getProgressColor(status)
}
}}
/>
Circular Progress (for compact views)
<Box position="relative" display="inline-flex">
<CircularProgress
variant="determinate"
value={progressPercentage}
size={60}
thickness={4}
sx={{ color: getProgressColor(status) }}
/>
<Box
position="absolute"
top={0}
left={0}
bottom={0}
right={0}
display="flex"
alignItems="center"
justifyContent="center"
>
<Typography variant="caption" component="div">
{`${Math.round(progressPercentage)}%`}
</Typography>
</Box>
</Box>
Status Indicators
Status Colors
const statusColors = {
NOT_STARTED: '#9E9E9E', // Grey
ON_TRACK: '#4CAF50', // Green
AT_RISK: '#FF9800', // Orange
OFF_TRACK: '#F44336', // Red
ACHIEVED: '#2196F3', // Blue
MISSED: '#795548' // Brown
};
Status Chip Component
<Chip
label={getStatusLabel(status)}
size="small"
sx={{
backgroundColor: alpha(statusColors[status], 0.1),
color: statusColors[status],
fontWeight: 'medium'
}}
/>
Layout Patterns
Goals Dashboard
<Grid container spacing={3}>
{/* Summary Cards */}
<Grid item xs={12}>
<Grid container spacing={2}>
<Grid item xs={12} sm={6} md={3}>
<SummaryCard title="Active Goals" value={activeCount} />
</Grid>
<Grid item xs={12} sm={6} md={3}>
<SummaryCard title="On Track" value={onTrackCount} color="success" />
</Grid>
<Grid item xs={12} sm={6} md={3}>
<SummaryCard title="At Risk" value={atRiskCount} color="warning" />
</Grid>
<Grid item xs={12} sm={6} md={3}>
<SummaryCard title="Achieved" value={achievedCount} color="primary" />
</Grid>
</Grid>
</Grid>
{/* Goals List */}
<Grid item xs={12} md={8}>
<Paper>
<GoalsList goals={goals} />
</Paper>
</Grid>
{/* Recent Updates */}
<Grid item xs={12} md={4}>
<Paper>
<RecentGoalUpdates />
</Paper>
</Grid>
</Grid>
Goal Details Page
<Container maxWidth="lg">
<Grid container spacing={3}>
{/* Header */}
<Grid item xs={12}>
<GoalHeader goal={goal} onEdit={handleEdit} />
</Grid>
{/* Main Content */}
<Grid item xs={12} md={8}>
<Stack spacing={3}>
<ProgressSection goal={goal} />
<ActivityBreakdown goal={goal} />
<TimelineChart goal={goal} />
</Stack>
</Grid>
{/* Sidebar */}
<Grid item xs={12} md={4}>
<Stack spacing={3}>
<DetailsCard goal={goal} />
<AssigneesCard goal={goal} />
<StatusUpdatesCard goal={goal} />
</Stack>
</Grid>
</Grid>
</Container>
Interactive Elements
Date Selection
Use the DatePickerWithPresets
component:
<DatePickerWithPresets
context="goal"
type="start"
value={startDate}
onChange={setStartDate}
presets={[
{ label: 'This Week', getValue: () => startOfWeek(new Date()) },
{ label: 'This Month', getValue: () => startOfMonth(new Date()) },
{ label: 'This Quarter', getValue: () => startOfQuarter(new Date()) },
{ label: 'This Year', getValue: () => startOfYear(new Date()) }
]}
/>
Entity Selection (Pillars/Activities)
<Autocomplete
multiple
options={activities}
value={selectedActivities}
onChange={(_, newValue) => setSelectedActivities(newValue)}
groupBy={(option) => option.pillarName}
getOptionLabel={(option) => option.name}
renderInput={(params) => (
<TextField
{...params}
label="Select Activities to Track"
placeholder="Search activities..."
/>
)}
renderGroup={(params) => (
<li key={params.key}>
<GroupHeader>{params.group}</GroupHeader>
<GroupItems>{params.children}</GroupItems>
</li>
)}
/>
Mobile Responsiveness
Responsive Goal Card
<Card sx={{
p: { xs: 1.5, sm: 2 },
'& .MuiTypography-h6': {
fontSize: { xs: '1rem', sm: '1.25rem' }
}
}}>
<Stack spacing={{ xs: 1, sm: 2 }}>
{/* Content adapts to screen size */}
</Stack>
</Card>
Mobile Navigation
<BottomNavigation value={activeTab} onChange={handleTabChange}>
<BottomNavigationAction label="Active" icon={<TrendingUpIcon />} />
<BottomNavigationAction label="Completed" icon={<CheckCircleIcon />} />
<BottomNavigationAction label="All Goals" icon={<ListIcon />} />
</BottomNavigation>
Loading States
Skeleton Loading
const GoalCardSkeleton = () => (
<Card sx={{ p: 2 }}>
<Skeleton variant="text" width="60%" height={32} />
<Skeleton variant="text" width="100%" height={20} />
<Skeleton variant="rectangular" width="100%" height={8} sx={{ my: 2 }} />
<Box display="flex" justifyContent="space-between">
<Skeleton variant="circular" width={32} height={32} />
<Skeleton variant="text" width={100} />
</Box>
</Card>
);
Progressive Loading
{loading ? (
<Grid container spacing={2}>
{[...Array(6)].map((_, i) => (
<Grid item xs={12} sm={6} md={4} key={i}>
<GoalCardSkeleton />
</Grid>
))}
</Grid>
) : (
<GoalsList goals={goals} />
)}
Empty States
No Goals
<EmptyState
icon={<TargetIcon sx={{ fontSize: 64 }} />}
title="No goals yet"
description="Set your first goal to start tracking your progress"
action={
<Button
variant="contained"
startIcon={<AddIcon />}
onClick={handleCreateGoal}
>
Create Your First Goal
</Button>
}
/>
Error Handling
Error States
<Alert severity="error" sx={{ mb: 2 }}>
<AlertTitle>Unable to load goals</AlertTitle>
Please check your connection and try again.
<Button size="small" onClick={retry} sx={{ mt: 1 }}>
Retry
</Button>
</Alert>
Accessibility
ARIA Labels
<IconButton
aria-label="Edit goal"
onClick={handleEdit}
>
<EditIcon />
</IconButton>
<LinearProgress
aria-label={`Goal progress: ${progressPercentage}%`}
aria-valuenow={progressPercentage}
aria-valuemin={0}
aria-valuemax={100}
/>
Keyboard Navigation
- Tab through interactive elements
- Enter/Space to activate buttons
- Escape to close modals
- Arrow keys in selection lists
Animation Guidelines
Progress Updates
<LinearProgress
variant="determinate"
value={progressPercentage}
sx={{
'& .MuiLinearProgress-bar': {
transition: 'transform 0.4s ease-in-out'
}
}}
/>
Status Changes
<Fade in={showStatusChange} timeout={300}>
<Alert severity="success">
Goal status updated to {newStatus}
</Alert>
</Fade>
Dark Mode Support
const GoalCard = styled(Card)(({ theme }) => ({
backgroundColor: theme.palette.mode === 'dark'
? theme.palette.grey[900]
: theme.palette.background.paper,
'&:hover': {
backgroundColor: theme.palette.mode === 'dark'
? theme.palette.grey[800]
: theme.palette.grey[50]
}
}));
Component Library
Reusable Goal Components
// Export from @/components/goals/
export { GoalCard } from './GoalCard';
export { GoalProgressBar } from './GoalProgressBar';
export { GoalStatusChip } from './GoalStatusChip';
export { GoalCreationWizard } from './GoalCreationWizard';
export { GoalDetailsPanel } from './GoalDetailsPanel';
export { GoalActivityPicker } from './GoalActivityPicker';
export { GoalDateRangePicker } from './GoalDateRangePicker';
export { GoalAssigneeSelector } from './GoalAssigneeSelector';
Best Practices
- Consistent Spacing: Use theme spacing units (multiples of 8px)
- Color Usage: Use semantic colors from theme palette
- Typography: Follow established type hierarchy
- Icons: Use Material Icons consistently
- Feedback: Provide immediate visual feedback for all actions
- Performance: Virtualize long lists, lazy load charts
- Responsive: Test on all screen sizes
- Accessibility: Include proper ARIA labels and keyboard support
For more UI guidelines, refer to the Material-UI documentation (opens in a new tab) and the main UI Guidelines section.