Files
LearnIT/docs/UI_DESIGN_GUIDELINES.md

1463 lines
39 KiB
Markdown

# UI Design Guidelines - AI Study Assistant
## 🎨 DESIGN SYSTEM OVERVIEW
### Core Design Philosophy
- **Clean & Modern**: Minimalist interface with purposeful use of color and space
- **Educational Focus**: UI elements designed to support learning, not distract
- **Accessibility First**: High contrast ratios, readable fonts, and clear visual hierarchy
- **Responsive Design**: Seamless experience across mobile, tablet, and web
---
## 🎨 COLOR PALETTE
### Primary Colors (New EPVC Color Scheme)
```dart
class AppColors {
// Primary Brand Colors
static const Color primaryTeal = Color(0xFF82C9BD); // Main teal color - PRIMARY
static const Color primaryOrange = Color(0xFFF68D2D); // Accent orange - SECONDARY
// Gradient Colors
static const Color gradientStart = Color(0xFF82C9BD); // Teal gradient start
static const Color gradientEnd = Color(0xFF6AB8A8); // Darker teal gradient end
// Secondary Colors
static const Color secondaryTeal = Color(0xFF6AB8A8); // Darker teal
static const Color accentTeal = Color(0xFF5AA69A); // Lighter teal accent
static const Color lightOrange = Color(0xFFF7A960); // Lighter orange
// Neutral Colors
static const Color background = Color(0xFFF8F9FA); // Light gray background
static const Color surface = Color(0xFFFFFFFF); // White surfaces
static const Color cardBackground = Color(0xFFFFFFFF); // White cards
// Text Colors
static const Color textPrimary = Color(0xFF1A1A1A); // Primary text
static const Color textSecondary = Color(0xFF6B7280); // Secondary text
static const Color textHint = Color(0xFF9CA3AF); // Hint text
// Status Colors
static const Color success = Color(0xFF10B981); // Green for success
static const Color warning = Color(0xFFF59E0B); // Amber for warnings
static const Color error = Color(0xFFEF4444); // Red for errors
static const Color info = Color(0xFF3B82F6); // Blue for info
// Interactive Colors
static const Color buttonPrimary = Color(0xFF82C9BD); // Primary button (teal)
static const Color buttonAccent = Color(0xFFF68D2D); // Accent button (orange)
static const Color buttonSecondary = Color(0xFFE5E7EB); // Secondary button
static const Color iconActive = Color(0xFF82C9BD); // Active icons (teal)
static const Color iconInactive = Color(0xFF9CA3AF); // Inactive icons
// Chat Specific Colors
static const Color chatBubbleStudent = Color(0xFF82C9BD); // Student messages (teal)
static const Color chatBubbleAI = Color(0xFFF3F4F6); // AI messages
static const Color chatInputBackground = Color(0xFFF8F9FA); // Input background
static const Color chatSendButton = Color(0xFF82C9BD); // Send button (teal)
}
```
### Color Usage Guidelines
#### Primary Colors (70% usage)
- **Primary Teal (#82C9BD)**: Main actions, navigation, important CTAs, backgrounds, frequent UI elements
- **Primary Orange (#F68D2D)**: Accent buttons, highlights, achievements, important notifications (used sparingly)
- **Secondary Teal**: Hover states, secondary actions, supporting elements
#### Neutral Colors (25% usage)
- **Background**: Page backgrounds, card backgrounds
- **Surface**: Cards, modals, dropdowns
- **Text Colors**: Hierarchical text organization
#### Status Colors (5% usage)
- **Success**: Quiz completion, correct answers
- **Warning**: Low mastery, upcoming deadlines
- **Error**: Network errors, validation failures
- **Info**: System notifications, help text
#### Color Distribution Strategy
- **Teal (#82C9BD)**: 60% of color usage - Primary brand color for backgrounds, main actions, navigation
- **Orange (#F68D2D)**: 10% of color usage - Accent color for CTAs, highlights, special buttons
- **Neutral Colors**: 30% of color usage - Backgrounds, surfaces, text hierarchy
---
## 📝 TYPOGRAPHY
### Font Family
```dart
class AppTypography {
// Primary Font Family (System fonts for consistency)
static const String primaryFont = 'SF Pro Display'; // iOS
static const String secondaryFont = 'Roboto'; // Android
static const String webFont = 'Inter'; // Web
// Font Weights
static const FontWeight thin = FontWeight.w100;
static const FontWeight light = FontWeight.w300;
static const FontWeight regular = FontWeight.w400;
static const FontWeight medium = FontWeight.w500;
static const FontWeight semiBold = FontWeight.w600;
static const FontWeight bold = FontWeight.w700;
static const FontWeight extraBold = FontWeight.w800;
}
```
### Text Styles
```dart
class AppTextStyles {
// Headings
static const TextStyle h1 = TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
color: AppColors.textPrimary,
height: 1.2,
);
static const TextStyle h2 = TextStyle(
fontSize: 24,
fontWeight: FontWeight.semiBold,
color: AppColors.textPrimary,
height: 1.3,
);
static const TextStyle h3 = TextStyle(
fontSize: 20,
fontWeight: FontWeight.semiBold,
color: AppColors.textPrimary,
height: 1.4,
);
// Body Text
static const TextStyle bodyLarge = TextStyle(
fontSize: 16,
fontWeight: FontWeight.regular,
color: AppColors.textPrimary,
height: 1.5,
);
static const TextStyle bodyMedium = TextStyle(
fontSize: 14,
fontWeight: FontWeight.regular,
color: AppColors.textPrimary,
height: 1.5,
);
static const TextStyle bodySmall = TextStyle(
fontSize: 12,
fontWeight: FontWeight.regular,
color: AppColors.textSecondary,
height: 1.4,
);
// Button Text
static const TextStyle buttonLarge = TextStyle(
fontSize: 16,
fontWeight: FontWeight.semiBold,
color: Colors.white,
height: 1.2,
);
static const TextStyle buttonMedium = TextStyle(
fontSize: 14,
fontWeight: FontWeight.medium,
color: Colors.white,
height: 1.2,
);
// Caption and Label
static const TextStyle caption = TextStyle(
fontSize: 11,
fontWeight: FontWeight.regular,
color: AppColors.textHint,
height: 1.3,
);
static const TextStyle label = TextStyle(
fontSize: 12,
fontWeight: FontWeight.medium,
color: AppColors.textSecondary,
height: 1.3,
);
}
```
---
## 🎯 ICONOGRAPHY
### Icon Sets
- **Primary**: Cupertino Icons (iOS-style consistency)
- **Secondary**: Material Icons (for Android compatibility)
- **Custom**: Educational icons for specific features
### Icon Guidelines
- **Size**: 24px for standard icons, 16px for compact, 32px for large
- **Weight**: Medium (consistent across the app)
- **Color**: Use `iconActive` for active states, `iconInactive` for inactive
- **Meaning**: Clear, universally understood symbols
### Key Icons
```dart
class AppIcons {
// Navigation
static const IconData home = CupertinoIcons.home;
static const IconData search = CupertinoIcons.search;
static const IconData profile = CupertinoIcons.person_circle;
static const IconData settings = CupertinoIcons.settings;
static const IconData menu = CupertinoIcons.bars;
// Actions
static const IconData send = CupertinoIcons.paperplane_fill;
static const IconData add = CupertinoIcons.add;
static const IconData edit = CupertinoIcons.pencil;
static const IconData delete = CupertinoIcons.trash;
static const IconData check = CupertinoIcons.check_mark;
// Educational
static const IconData book = CupertinoIcons.book;
static const IconData quiz = CupertinoIcons.question_circle;
static const IconData progress = CupertinoIcons.chart_bar;
static const IconData lightbulb = CupertinoIcons.lightbulb;
static const IconData trophy = CupertinoIcons.trophy;
// Status
static const IconData success = CupertinoIcons.check_mark_circled;
static const IconData warning = CupertinoIcons.exclamationmark_triangle;
static const IconData error = CupertinoIcons.xmark_circle;
static const IconData info = CupertinoIcons.info_circle;
}
```
---
## 📱 COMPONENTS
### Buttons
#### Primary Button
```dart
class PrimaryButton extends StatelessWidget {
final String text;
final VoidCallback onPressed;
final bool isLoading;
final bool isDisabled;
const PrimaryButton({
Key? key,
required this.text,
required this.onPressed,
this.isLoading = false,
this.isDisabled = false,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return AnimatedContainer(
duration: const Duration(milliseconds: 200),
height: 48,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: isDisabled
? [AppColors.buttonSecondary, AppColors.buttonSecondary]
: [AppColors.gradientStart, AppColors.gradientEnd],
),
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: AppColors.primaryBlue.withOpacity(0.2),
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
child: Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.circular(12),
onTap: isDisabled || isLoading ? null : onPressed,
child: Center(
child: isLoading
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation(Colors.white),
),
)
: Text(
text,
style: AppTextStyles.buttonLarge,
),
),
),
),
);
}
}
```
#### Secondary Button
```dart
class SecondaryButton extends StatelessWidget {
final String text;
final VoidCallback onPressed;
final bool isDisabled;
const SecondaryButton({
Key? key,
required this.text,
required this.onPressed,
this.isDisabled = false,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
height: 48,
decoration: BoxDecoration(
color: AppColors.buttonSecondary,
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: AppColors.primaryBlue.withOpacity(0.3),
width: 1,
),
),
child: Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.circular(12),
onTap: isDisabled ? null : onPressed,
child: Center(
child: Text(
text,
style: AppTextStyles.buttonLarge.copyWith(
color: isDisabled
? AppColors.textHint
: AppColors.primaryBlue,
),
),
),
),
),
);
}
}
```
### Cards
#### Standard Card
```dart
class StandardCard extends StatelessWidget {
final Widget child;
final EdgeInsetsGeometry? padding;
final EdgeInsetsGeometry? margin;
final VoidCallback? onTap;
final bool hasShadow;
const StandardCard({
required this.child,
this.padding,
this.onTap,
});
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: AppColors.surface,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.08),
blurRadius: 8,
offset: Offset(0, 2),
),
BoxShadow(
color: Colors.black.withOpacity(0.04),
blurRadius: 1,
offset: Offset(0, 1),
),
],
),
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(16),
child: Padding(
padding: padding ?? EdgeInsets.all(16),
child: child,
),
),
),
);
}
}
```
#### Interactive Card
```dart
class InteractiveCard extends StatefulWidget {
final Widget child;
final EdgeInsetsGeometry? padding;
final VoidCallback? onTap;
final bool isSelected;
const InteractiveCard({
required this.child,
this.padding,
this.onTap,
this.isSelected = false,
});
@override
State<InteractiveCard> createState() => _InteractiveCardState();
}
class _InteractiveCardState extends State<InteractiveCard> {
bool isHovered = false;
@override
Widget build(BuildContext context) {
return MouseRegion(
onEnter: (_) => setState(() => isHovered = true),
onExit: (_) => setState(() => isHovered = false),
child: AnimatedContainer(
duration: Duration(milliseconds: 200),
decoration: BoxDecoration(
color: widget.isSelected
? AppColors.primaryBlue.withOpacity(0.1)
: AppColors.surface,
borderRadius: BorderRadius.circular(16),
border: widget.isSelected
? Border.all(color: AppColors.primaryBlue, width: 2)
: null,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(isHovered ? 0.12 : 0.08),
blurRadius: isHovered ? 12 : 8,
offset: Offset(0, isHovered ? 4 : 2),
),
BoxShadow(
color: Colors.black.withOpacity(0.04),
blurRadius: 1,
offset: Offset(0, 1),
),
],
),
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: widget.onTap,
borderRadius: BorderRadius.circular(16),
child: Padding(
padding: widget.padding ?? EdgeInsets.all(16),
child: widget.child,
),
),
),
),
);
}
}
```
### Input Fields
#### Text Input
```dart
class AppTextInput extends StatelessWidget {
final String? label;
final String? hint;
final String? errorText;
final TextEditingController controller;
final bool obscureText;
final TextInputType keyboardType;
final VoidCallback? onTap;
final ValueChanged<String>? onChanged;
final bool isDisabled;
const AppTextInput({
Key? key,
this.label,
this.hint,
this.errorText,
required this.controller,
this.obscureText = false,
this.keyboardType = TextInputType.text,
this.onTap,
this.onChanged,
this.isDisabled = false,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (label != null) ...[
Text(
label!,
style: AppTextStyles.label,
),
const SizedBox(height: 8),
],
Container(
decoration: BoxDecoration(
color: isDisabled ? AppColors.buttonSecondary : AppColors.surface,
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: errorText != null
? AppColors.error
: AppColors.primaryBlue.withOpacity(0.3),
width: 1,
),
),
child: TextField(
controller: controller,
obscureText: obscureText,
keyboardType: keyboardType,
onTap: onTap,
onChanged: onChanged,
enabled: !isDisabled,
decoration: InputDecoration(
hintText: hint,
hintStyle: AppTextStyles.bodyMedium.copyWith(
color: AppColors.textHint,
),
border: InputBorder.none,
contentPadding: const EdgeInsets.all(16),
),
style: AppTextStyles.bodyMedium,
),
),
if (errorText != null) ...[
const SizedBox(height: 4),
Text(
errorText!,
style: AppTextStyles.caption.copyWith(
color: AppColors.error,
),
),
],
],
);
}
}
```
---
## 📱 MODERN LAYOUT PATTERNS
### Dashboard Layout
Based on the reference design, implement a modern card-based dashboard with:
#### Grid Layout System
```dart
class DashboardGrid extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(16),
child: GridView.count(
crossAxisCount: 2,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
childAspectRatio: 1.2,
children: [
_buildQuickActionCard(
icon: Icons.chat,
title: "Ask Tutor",
subtitle: "Get help with your questions",
color: AppColors.primaryBlue,
onTap: () => _navigateToChat(),
),
_buildQuickActionCard(
icon: Icons.quiz,
title: "Take Quiz",
subtitle: "Test your knowledge",
color: AppColors.primaryTeal,
onTap: () => _navigateToQuiz(),
),
_buildQuickActionCard(
icon: Icons.trending_up,
title: "Progress",
subtitle: "Track your learning",
color: AppColors.primaryOrange,
onTap: () => _navigateToProgress(),
),
_buildQuickActionCard(
icon: Icons.book,
title: "Study Materials",
subtitle: "Access learning resources",
color: AppColors.secondaryBlue,
onTap: () => _navigateToMaterials(),
),
],
),
);
}
Widget _buildQuickActionCard({
required IconData icon,
required String title,
required String subtitle,
required Color color,
required VoidCallback onTap,
}) {
return InteractiveCard(
onTap: onTap,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 48,
height: 48,
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: Icon(
icon,
color: color,
size: 24,
),
),
SizedBox(height: 12),
Text(
title,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColors.textPrimary,
),
),
SizedBox(height: 4),
Text(
subtitle,
style: TextStyle(
fontSize: 12,
color: AppColors.textSecondary,
),
),
],
),
);
}
}
```
#### Content Cards
```dart
class ContentCard extends StatelessWidget {
final String title;
final String description;
final String progress;
final Color accentColor;
final VoidCallback onTap;
const ContentCard({
required this.title,
required this.description,
required this.progress,
required this.accentColor,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return InteractiveCard(
onTap: onTap,
padding: EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: AppColors.textPrimary,
),
),
SizedBox(height: 8),
Text(
description,
style: TextStyle(
fontSize: 14,
color: AppColors.textSecondary,
),
),
],
),
),
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: accentColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(20),
),
child: Icon(
Icons.arrow_forward,
color: accentColor,
size: 20,
),
),
],
),
SizedBox(height: 16),
Container(
height: 4,
decoration: BoxDecoration(
color: AppColors.buttonSecondary,
borderRadius: BorderRadius.circular(2),
),
child: FractionallySizedBox(
alignment: Alignment.centerLeft,
widthFactor: double.parse(progress),
child: Container(
decoration: BoxDecoration(
color: accentColor,
borderRadius: BorderRadius.circular(2),
),
),
),
),
SizedBox(height: 8),
Text(
'${(double.parse(progress) * 100).toInt()}% Complete',
style: TextStyle(
fontSize: 12,
color: AppColors.textHint,
),
),
],
),
);
}
}
```
#### Navigation Bar
```dart
class ModernBottomNavigation extends StatelessWidget {
final int currentIndex;
final ValueChanged<int> onTap;
const ModernBottomNavigation({
required this.currentIndex,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: AppColors.surface,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 10,
offset: Offset(0, -2),
),
],
),
child: SafeArea(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildNavItem(
icon: Icons.home,
label: "Home",
index: 0,
),
_buildNavItem(
icon: Icons.chat,
label: "Chat",
index: 1,
),
_buildNavItem(
icon: Icons.quiz,
label: "Quiz",
index: 2,
),
_buildNavItem(
icon: Icons.person,
label: "Profile",
index: 3,
),
],
),
),
),
);
}
Widget _buildNavItem({
required IconData icon,
required String label,
required int index,
}) {
final isActive = currentIndex == index;
final color = isActive ? AppColors.primaryBlue : AppColors.iconInactive;
return GestureDetector(
onTap: () => onTap(index),
child: Container(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration(
color: isActive ? AppColors.primaryBlue.withOpacity(0.1) : Colors.transparent,
borderRadius: BorderRadius.circular(12),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
icon,
color: color,
size: 24,
),
SizedBox(height: 4),
Text(
label,
style: TextStyle(
color: color,
fontSize: 12,
fontWeight: isActive ? FontWeight.w600 : FontWeight.normal,
),
),
],
),
),
);
}
}
```
### Chat Interface
```dart
class ModernChatInterface extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
// Chat Header
Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
color: AppColors.surface,
border: Border(
bottom: BorderSide(
color: AppColors.buttonSecondary,
width: 1,
),
),
),
child: Row(
children: [
CircleAvatar(
backgroundColor: AppColors.primaryBlue.withOpacity(0.1),
child: Icon(
Icons.smart_toy,
color: AppColors.primaryBlue,
),
),
SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"AI Tutor",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColors.textPrimary,
),
),
Text(
"Always here to help",
style: TextStyle(
fontSize: 12,
color: AppColors.textSecondary,
),
),
],
),
),
Icon(
Icons.more_vert,
color: AppColors.iconInactive,
),
],
),
),
// Chat Messages
Expanded(
child: Container(
color: AppColors.background,
child: ListView.builder(
padding: EdgeInsets.all(16),
itemCount: messages.length,
itemBuilder: (context, index) {
final message = messages[index];
return _buildMessageBubble(message);
},
),
),
),
// Chat Input
Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
color: AppColors.surface,
border: Border(
top: BorderSide(
color: AppColors.buttonSecondary,
width: 1,
),
),
),
child: Row(
children: [
Expanded(
child: Container(
decoration: BoxDecoration(
color: AppColors.chatInputBackground,
borderRadius: BorderRadius.circular(24),
border: Border.all(
color: AppColors.primaryBlue.withOpacity(0.3),
width: 1,
),
),
child: TextField(
decoration: InputDecoration(
hintText: "Ask your question...",
hintStyle: TextStyle(
color: AppColors.textHint,
),
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
horizontal: 16,
vertical: 12,
),
),
),
),
),
SizedBox(width: 12),
Container(
width: 48,
height: 48,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [AppColors.primaryBlue, AppColors.primaryTeal],
begin: Alignment.centerLeft,
end: Alignment.centerRight,
),
borderRadius: BorderRadius.circular(24),
),
child: Icon(
Icons.send,
color: Colors.white,
size: 20,
),
),
],
),
),
],
);
}
Widget _buildMessageBubble(Message message) {
final isUser = message.isUser;
return Container(
margin: EdgeInsets.only(bottom: 16),
child: Row(
mainAxisAlignment: isUser ? MainAxisAlignment.end : MainAxisAlignment.start,
children: [
if (!isUser) ...[
CircleAvatar(
radius: 16,
backgroundColor: AppColors.primaryBlue.withOpacity(0.1),
child: Icon(
Icons.smart_toy,
color: AppColors.primaryBlue,
size: 16,
),
),
SizedBox(width: 8),
],
Flexible(
child: Container(
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width * 0.7,
),
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: isUser ? AppColors.primaryBlue : AppColors.chatBubbleAI,
borderRadius: BorderRadius.circular(16),
),
child: Text(
message.text,
style: TextStyle(
color: isUser ? Colors.white : AppColors.textPrimary,
fontSize: 14,
),
),
),
),
if (isUser) ...[
SizedBox(width: 8),
CircleAvatar(
radius: 16,
backgroundColor: AppColors.primaryOrange.withOpacity(0.1),
child: Icon(
Icons.person,
color: AppColors.primaryOrange,
size: 16,
),
),
],
],
),
);
}
}
```
---
## 🎭 ANIMATIONS
### Animation Principles
- **Purposeful**: Every animation should enhance user understanding
- **Smooth**: Use easing functions that feel natural
- **Fast**: Respect user time, keep animations under 300ms
- **Consistent**: Use similar timing across the app
### Standard Animation Durations
```dart
class AppAnimations {
static const Duration fast = Duration(milliseconds: 150);
static const Duration medium = Duration(milliseconds: 250);
static const Duration slow = Duration(milliseconds: 350);
static const Duration extraSlow = Duration(milliseconds: 500);
}
```
### Curves
```dart
class AppCurves {
static const Curve easeIn = Curves.easeIn;
static const Curve easeOut = Curves.easeOut;
static const Curve easeInOut = Curves.easeInOut;
static const Curve bounceIn = Curves.bounceIn;
static const Curve elasticOut = Curves.elasticOut;
}
```
### Common Animations
#### Fade Transition
```dart
class FadeIn extends StatefulWidget {
final Widget child;
final Duration duration;
const FadeIn({
Key? key,
required this.child,
this.duration = AppAnimations.medium,
}) : super(key: key);
@override
State<FadeIn> createState() => _FadeInState();
}
class _FadeInState extends State<FadeIn>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: widget.duration,
vsync: this,
);
_animation = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(CurvedAnimation(
parent: _controller,
curve: AppCurves.easeIn,
));
_controller.forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return FadeTransition(
opacity: _animation,
child: widget.child,
);
}
}
```
#### Slide Transition
```dart
class SlideIn extends StatefulWidget {
final Widget child;
final Duration duration;
final Offset begin;
final Offset end;
const SlideIn({
Key? key,
required this.child,
this.duration = AppAnimations.medium,
this.begin = const Offset(0, 0.3),
this.end = Offset.zero,
}) : super(key: key);
@override
State<SlideIn> createState() => _SlideInState();
}
class _SlideInState extends State<SlideIn>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<Offset> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: widget.duration,
vsync: this,
);
_animation = Tween<Offset>(
begin: widget.begin,
end: widget.end,
).animate(CurvedAnimation(
parent: _controller,
curve: AppCurves.easeOut,
));
_controller.forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return SlideTransition(
position: _animation,
child: widget.child,
);
}
}
```
#### Scale Animation
```dart
class ScaleIn extends StatefulWidget {
final Widget child;
final Duration duration;
const ScaleIn({
Key? key,
required this.child,
this.duration = AppAnimations.medium,
}) : super(key: key);
@override
State<ScaleIn> createState() => _ScaleInState();
}
class _ScaleInState extends State<ScaleIn>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: widget.duration,
vsync: this,
);
_animation = Tween<double>(
begin: 0.8,
end: 1.0,
).animate(CurvedAnimation(
parent: _controller,
curve: AppCurves.elasticOut,
));
_controller.forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ScaleTransition(
scale: _animation,
child: widget.child,
);
}
}
```
---
## 📐 LAYOUT GUIDELINES
### Spacing System
```dart
class AppSpacing {
// Base spacing unit (8px)
static const double xs = 4; // 0.5x
static const double sm = 8; // 1x
static const double md = 16; // 2x
static const double lg = 24; // 3x
static const double xl = 32; // 4x
static const double xxl = 48; // 6x
static const double xxxl = 64; // 8x
}
```
### Border Radius
```dart
class AppBorderRadius {
static const double sm = 8;
static const double md = 12;
static const double lg = 16;
static const double xl = 20;
static const double xxl = 24;
static const double round = 1000; // For circles
}
```
### Screen Breakpoints
```dart
class AppBreakpoints {
static const double mobile = 480;
static const double tablet = 768;
static const double desktop = 1024;
static const double largeDesktop = 1440;
}
```
---
## 🎨 SCREEN-SPECIFIC DESIGNS
### Login Screen
- **Background**: Gradient from `gradientStart` to `gradientEnd`
- **Logo**: Centered with fade-in animation
- **Form**: White card with subtle shadow
- **Buttons**: Primary gradient button for login, secondary for signup
### Student Dashboard
- **Header**: App bar with gradient background
- **Cards**: Grid layout with mastery progress bars
- **Navigation**: Bottom navigation with active state indicators
- **Animations**: Cards slide in from bottom on load
### Chat Interface
- **Background**: Light gray (`chatInputBackground`)
- **Messages**: Different colors for student vs AI
- **Input**: Rounded input field with send button
- **Animations**: Messages slide in from sides
### Quiz Screen
- **Timer**: Circular progress indicator
- **Questions**: Card-based layout
- **Options**: Radio buttons with hover states
- **Progress**: Linear progress bar at top
---
## 🌙 DARK MODE
### Dark Color Palette
```dart
class AppDarkColors {
// Background Colors
static const Color background = Color(0xFF121212);
static const Color surface = Color(0xFF1E1E1E);
static const Color cardBackground = Color(0xFF2D2D2D);
// Text Colors
static const Color textPrimary = Color(0xFFFFFFFF);
static const Color textSecondary = Color(0xFFB3B3B3);
static const Color textHint = Color(0xFF808080);
// Keep primary colors the same
static const Color primaryBlue = AppColors.primaryBlue;
static const Color primaryTeal = AppColors.primaryTeal;
static const Color primaryOrange = AppColors.primaryOrange;
}
```
### Implementation Guidelines
- **Automatic**: Follow system dark mode preference
- **Manual**: Toggle in app settings
- **Consistency**: All screens must support both themes
- **Animation**: Smooth transition between themes
---
## 📱 RESPONSIVE DESIGN
### Mobile (< 768px)
- **Layout**: Single column, scrollable
- **Navigation**: Bottom navigation bar
- **Cards**: Full width with horizontal padding
- **Typography**: Base sizes as defined
### Tablet (768px - 1024px)
- **Layout**: Two-column where appropriate
- **Navigation**: Side drawer or top navigation
- **Cards**: Grid layout (2-3 columns)
- **Typography**: Slightly larger base sizes
### Desktop (> 1024px)
- **Layout**: Multi-column, fixed width centered
- **Navigation**: Top navigation bar
- **Cards**: Grid layout (3-4 columns)
- **Typography**: Larger base sizes, better readability
---
## 🎯 ACCESSIBILITY
### Color Contrast
- **Normal Text**: Minimum 4.5:1 contrast ratio
- **Large Text**: Minimum 3:1 contrast ratio
- **Interactive Elements**: Minimum 3:1 contrast ratio
### Typography
- **Minimum Size**: 14px for body text
- **Line Height**: 1.5 for better readability
- **Font Weight**: Regular or medium for body text
### Focus States
- **Visible**: All interactive elements have visible focus states
- **Consistent**: Same focus style across the app
- **Keyboard**: Full keyboard navigation support
### Screen Reader Support
- **Labels**: All images and icons have alt text
- **Semantic**: Use semantic HTML elements
- **Announcements**: Important changes are announced
---
## 🚀 PERFORMANCE
### Animation Performance
- **60 FPS**: All animations should maintain 60 FPS
- **GPU**: Use GPU-accelerated animations where possible
- **Simplify**: Avoid complex animations on low-end devices
### Asset Optimization
- **Images**: WebP format with appropriate sizes
- **Icons**: SVG icons where possible
- **Fonts**: Subset fonts to reduce size
### Bundle Size
- **Tree Shaking**: Remove unused code
- **Lazy Loading**: Load features on demand
- **Compression**: Compress all assets
---
## 📋 DESIGN CHECKLIST
### Before Implementation
- [ ] Color palette defined and consistent
- [ ] Typography scale established
- [ ] Component library created
- [ ] Animation system implemented
- [ ] Accessibility guidelines reviewed
### During Implementation
- [ ] Components follow design system
- [ ] Animations are smooth and purposeful
- [ ] Responsive design works on all breakpoints
- [ ] Dark mode is supported
- [ ] Accessibility features are implemented
### Before Release
- [ ] All screens reviewed for consistency
- [ ] Performance tested on target devices
- [ ] Accessibility tested with screen readers
- [ ] User testing completed
- [ ] Design documentation updated
---
## 🔄 DESIGN SYSTEM MAINTENANCE
### Regular Updates
- **Monthly**: Review usage analytics and user feedback
- **Quarterly**: Update components and add new ones
- **Annually**: Major design system updates
### Version Control
- **Semantic Versioning**: Use semantic versioning for releases
- **Changelog**: Maintain detailed changelog
- **Migration**: Provide migration guides for breaking changes
### Documentation
- **Living Document**: Keep documentation up to date
- **Examples**: Provide code examples for all components
- **Guidelines**: Maintain usage guidelines and best practices
---
*Last Updated: 2026-05-06*
*Version: 1.0.0*
*Design System Owner: UI/UX Team*