Modernize Android UI design system to match iOS

- Create comprehensive design system with Theme.kt, Type.kt, and Shape.kt
- Update color palette to match iOS (primary #2563EB, accent #8B5CF6)
- Implement modern typography scale across all text styles
- Add consistent corner radius system (AppRadius)

UI Improvements:
- HomeScreen: Add personalized greeting, gradient icons, modern stats
- TaskCard: Pill-style badges, semantic colors, clean metadata
- ResidencesScreen: Gradient circular icons, location indicators
- LoginScreen: Gradient button background, modern card styling

Design Features:
- Gradient backgrounds for visual interest
- Semantic color coding for states
- Flat design with minimal elevation
- Consistent spacing and typography
- Material3 shapes throughout

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Trey t
2025-11-10 12:12:30 -06:00
parent e13448083c
commit 764a90cb41
7 changed files with 633 additions and 238 deletions

View File

@@ -1,6 +1,8 @@
package com.mycrib.android.ui.components.task
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
@@ -8,8 +10,11 @@ import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.mycrib.android.ui.theme.AppColors
import com.mycrib.shared.models.TaskDetail
import com.mycrib.shared.models.TaskCategory
import com.mycrib.shared.models.TaskPriority
@@ -32,10 +37,10 @@ fun TaskCard(
) {
Card(
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(16.dp),
elevation = CardDefaults.cardElevation(defaultElevation = 2.dp),
shape = MaterialTheme.shapes.large,
elevation = CardDefaults.cardElevation(defaultElevation = 0.dp),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surfaceContainerLowest
containerColor = MaterialTheme.colorScheme.surface
)
) {
Column(
@@ -51,54 +56,75 @@ fun TaskCard(
Column(modifier = Modifier.weight(1f)) {
Text(
text = task.title,
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Bold
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.onSurface
)
Spacer(modifier = Modifier.height(4.dp))
Spacer(modifier = Modifier.height(8.dp))
// Pill-style category badge
Surface(
color = MaterialTheme.colorScheme.secondaryContainer,
shape = RoundedCornerShape(8.dp)
color = MaterialTheme.colorScheme.surfaceVariant,
shape = RoundedCornerShape(12.dp)
) {
Text(
text = task.category.name,
modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp),
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.onSecondaryContainer
text = task.category.name.uppercase(),
modifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp),
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
Column(
horizontalAlignment = Alignment.End,
verticalArrangement = Arrangement.spacedBy(6.dp)
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Surface(
color = when (task.priority.name.lowercase()) {
"urgent" -> MaterialTheme.colorScheme.error
"high" -> MaterialTheme.colorScheme.errorContainer
"medium" -> MaterialTheme.colorScheme.primaryContainer
else -> MaterialTheme.colorScheme.surfaceVariant
},
shape = RoundedCornerShape(10.dp)
// Priority badge with semantic colors
val priorityColor = when (task.priority.name.lowercase()) {
"urgent", "high" -> Color(0xFFEF4444) // Error/Red
"medium" -> Color(0xFFF59E0B) // Warning/Orange
else -> Color(0xFF10B981) // Success/Green
}
Row(
modifier = Modifier
.background(
priorityColor.copy(alpha = 0.15f),
RoundedCornerShape(12.dp)
)
.padding(horizontal = 12.dp, vertical = 6.dp),
horizontalArrangement = Arrangement.spacedBy(4.dp),
verticalAlignment = Alignment.CenterVertically
) {
Box(
modifier = Modifier
.size(6.dp)
.clip(CircleShape)
.background(priorityColor)
)
Text(
text = task.priority.name.uppercase(),
modifier = Modifier.padding(horizontal = 10.dp, vertical = 6.dp),
style = MaterialTheme.typography.labelMedium,
fontWeight = FontWeight.Bold
style = MaterialTheme.typography.labelSmall,
color = priorityColor
)
}
// Status badge with semantic colors
if (task.status != null) {
val statusColor = when (task.status.name.lowercase()) {
"completed" -> AppColors.taskCompleted
"in_progress" -> AppColors.taskInProgress
"pending" -> Color(0xFFF59E0B)
"cancelled" -> AppColors.taskCanceled
else -> Color(0xFF9CA3AF)
}
Surface(
color = MaterialTheme.colorScheme.tertiaryContainer,
shape = RoundedCornerShape(10.dp)
color = statusColor.copy(alpha = 0.15f),
shape = RoundedCornerShape(12.dp)
) {
Text(
text = task.status.name.uppercase(),
modifier = Modifier.padding(horizontal = 10.dp, vertical = 6.dp),
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.onTertiaryContainer
text = task.status.name.replace("_", " ").uppercase(),
modifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp),
style = MaterialTheme.typography.labelSmall,
color = statusColor
)
}
}
@@ -114,42 +140,61 @@ fun TaskCard(
)
}
Spacer(modifier = Modifier.height(12.dp))
HorizontalDivider()
Spacer(modifier = Modifier.height(12.dp))
Spacer(modifier = Modifier.height(16.dp))
HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant)
Spacer(modifier = Modifier.height(16.dp))
// Metadata pills
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
Row(verticalAlignment = Alignment.CenterVertically) {
// Date pill
Row(
modifier = Modifier
.background(
MaterialTheme.colorScheme.surfaceVariant,
RoundedCornerShape(12.dp)
)
.padding(horizontal = 12.dp, vertical = 6.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(6.dp)
) {
Icon(
Icons.Default.CalendarToday,
contentDescription = null,
modifier = Modifier.size(16.dp),
tint = MaterialTheme.colorScheme.primary
modifier = Modifier.size(14.dp),
tint = MaterialTheme.colorScheme.onSurfaceVariant
)
Spacer(modifier = Modifier.width(4.dp))
Text(
text = task.nextScheduledDate ?: task.dueDate ?: "N/A",
style = MaterialTheme.typography.bodySmall,
fontWeight = FontWeight.SemiBold
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
// Cost pill
task.estimatedCost?.let {
Row(verticalAlignment = Alignment.CenterVertically) {
Row(
modifier = Modifier
.background(
MaterialTheme.colorScheme.surfaceVariant,
RoundedCornerShape(12.dp)
)
.padding(horizontal = 12.dp, vertical = 6.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(6.dp)
) {
Icon(
Icons.Default.AttachMoney,
contentDescription = null,
modifier = Modifier.size(16.dp),
tint = MaterialTheme.colorScheme.tertiary
modifier = Modifier.size(14.dp),
tint = MaterialTheme.colorScheme.onSurfaceVariant
)
Spacer(modifier = Modifier.width(4.dp))
Text(
text = "$$it",
style = MaterialTheme.typography.bodySmall,
fontWeight = FontWeight.SemiBold,
color = MaterialTheme.colorScheme.tertiary
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
@@ -282,7 +327,8 @@ fun CompletionCard(completion: TaskCompletion) {
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surfaceVariant
),
shape = RoundedCornerShape(12.dp)
shape = MaterialTheme.shapes.medium,
elevation = CardDefaults.cardElevation(defaultElevation = 0.dp)
) {
Column(
modifier = Modifier

View File

@@ -1,15 +1,22 @@
package com.mycrib.android.ui.screens
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.mycrib.android.ui.theme.AppColors
import com.mycrib.android.ui.theme.AppRadius
import com.mycrib.android.viewmodel.ResidenceViewModel
import com.mycrib.shared.network.ApiResult
@@ -30,10 +37,17 @@ fun HomeScreen(
Scaffold(
topBar = {
TopAppBar(
title = { Text("myCrib") },
title = { Text("myCrib", style = MaterialTheme.typography.headlineSmall) },
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.background
),
actions = {
IconButton(onClick = onLogout) {
Icon(Icons.Default.ExitToApp, contentDescription = "Logout")
Icon(
Icons.Default.ExitToApp,
contentDescription = "Logout",
tint = MaterialTheme.colorScheme.primary
)
}
}
)
@@ -42,71 +56,116 @@ fun HomeScreen(
Column(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colorScheme.background)
.padding(paddingValues)
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
.padding(horizontal = 16.dp, vertical = 12.dp),
verticalArrangement = Arrangement.spacedBy(20.dp)
) {
// Personalized Greeting
Column(
modifier = Modifier.padding(vertical = 8.dp)
) {
Text(
text = "Welcome back",
style = MaterialTheme.typography.titleLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Text(
text = "Manage your properties",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
// Summary Card
when (summaryState) {
is ApiResult.Success -> {
val summary = (summaryState as ApiResult.Success).data
Card(
modifier = Modifier.fillMaxWidth(),
shape = MaterialTheme.shapes.large,
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.primaryContainer
)
containerColor = MaterialTheme.colorScheme.surface
),
elevation = CardDefaults.cardElevation(defaultElevation = 0.dp)
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
.padding(20.dp)
) {
Text(
text = "Overview",
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.onPrimaryContainer
)
Spacer(modifier = Modifier.height(8.dp))
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
// Gradient circular icon
Box(
modifier = Modifier
.size(44.dp)
.clip(CircleShape)
.background(
Brush.linearGradient(
listOf(
Color(0xFF2563EB),
Color(0xFF8B5CF6)
)
)
),
contentAlignment = Alignment.Center
) {
Icon(
Icons.Default.Home,
contentDescription = null,
tint = Color.White,
modifier = Modifier.size(24.dp)
)
}
Column {
Text(
text = "Overview",
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.onSurface
)
Text(
text = "Your property stats",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
Spacer(modifier = Modifier.height(20.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(
text = "${summary.residences.size}",
style = MaterialTheme.typography.headlineMedium,
color = MaterialTheme.colorScheme.onPrimaryContainer
)
Text(
text = "Properties",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onPrimaryContainer
)
}
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(
text = "${summary.summary.totalTasks}",
style = MaterialTheme.typography.headlineMedium,
color = MaterialTheme.colorScheme.onPrimaryContainer
)
Text(
text = "Total Tasks",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onPrimaryContainer
)
}
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(
text = "${summary.summary.totalPending}",
style = MaterialTheme.typography.headlineMedium,
color = MaterialTheme.colorScheme.onPrimaryContainer
)
Text(
text = "Pending",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onPrimaryContainer
)
}
StatItem(
value = "${summary.residences.size}",
label = "Properties",
color = Color(0xFF3B82F6)
)
Divider(
modifier = Modifier
.height(48.dp)
.width(1.dp),
color = MaterialTheme.colorScheme.outlineVariant
)
StatItem(
value = "${summary.summary.totalTasks}",
label = "Total Tasks",
color = Color(0xFF8B5CF6)
)
Divider(
modifier = Modifier
.height(48.dp)
.width(1.dp),
color = MaterialTheme.colorScheme.outlineVariant
)
StatItem(
value = "${summary.summary.totalPending}",
label = "Pending",
color = Color(0xFFF59E0B)
)
}
}
}
@@ -131,66 +190,126 @@ fun HomeScreen(
}
// Residences Card
Card(
modifier = Modifier
.fillMaxWidth()
.clickable { onNavigateToResidences() }
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(24.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
Icons.Default.Home,
contentDescription = null,
modifier = Modifier.size(48.dp)
)
Spacer(modifier = Modifier.width(16.dp))
Column {
Text(
text = "Residences",
style = MaterialTheme.typography.titleLarge
)
Text(
text = "Manage your properties",
style = MaterialTheme.typography.bodyMedium
)
}
}
}
NavigationCard(
title = "Properties",
subtitle = "Manage your residences",
icon = Icons.Default.Home,
iconColor = Color(0xFF3B82F6),
onClick = onNavigateToResidences
)
// Tasks Card
Card(
modifier = Modifier
.fillMaxWidth()
.clickable { onNavigateToTasks() }
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(24.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
Icons.Default.CheckCircle,
contentDescription = null,
modifier = Modifier.size(48.dp)
)
Spacer(modifier = Modifier.width(16.dp))
Column {
Text(
text = "Tasks",
style = MaterialTheme.typography.titleLarge
)
Text(
text = "View and manage tasks",
style = MaterialTheme.typography.bodyMedium
)
}
}
}
NavigationCard(
title = "Tasks",
subtitle = "View and manage tasks",
icon = Icons.Default.CheckCircle,
iconColor = Color(0xFF10B981),
onClick = onNavigateToTasks
)
}
}
}
@Composable
private fun StatItem(
value: String,
label: String,
color: Color
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
Box(
modifier = Modifier
.size(36.dp)
.clip(CircleShape)
.background(color.copy(alpha = 0.1f)),
contentAlignment = Alignment.Center
) {
Text(
text = value,
style = MaterialTheme.typography.titleLarge,
color = color
)
}
Text(
text = label,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
@Composable
private fun NavigationCard(
title: String,
subtitle: String,
icon: androidx.compose.ui.graphics.vector.ImageVector,
iconColor: Color,
onClick: () -> Unit
) {
Card(
modifier = Modifier
.fillMaxWidth()
.clickable { onClick() },
shape = MaterialTheme.shapes.large,
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surface
),
elevation = CardDefaults.cardElevation(defaultElevation = 0.dp)
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(20.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
// Gradient circular icon
Box(
modifier = Modifier
.size(56.dp)
.clip(CircleShape)
.background(
Brush.linearGradient(
listOf(
iconColor,
iconColor.copy(alpha = 0.7f)
)
)
),
contentAlignment = Alignment.Center
) {
Icon(
icon,
contentDescription = null,
tint = Color.White,
modifier = Modifier.size(28.dp)
)
}
Column(
modifier = Modifier.weight(1f)
) {
Text(
text = title,
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.onSurface
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = subtitle,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
Icon(
Icons.Default.KeyboardArrowRight,
contentDescription = null,
tint = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
}

View File

@@ -2,6 +2,7 @@ package com.mycrib.android.ui.screens
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
@@ -11,6 +12,8 @@ import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
@@ -63,11 +66,11 @@ fun LoginScreen(
modifier = Modifier
.fillMaxWidth(0.9f)
.wrapContentHeight(),
shape = RoundedCornerShape(24.dp),
shape = MaterialTheme.shapes.extraLarge,
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surface
),
elevation = CardDefaults.cardElevation(defaultElevation = 8.dp)
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
) {
Column(
modifier = Modifier
@@ -123,28 +126,54 @@ fun LoginScreen(
ErrorCard(message = errorMessage)
Button(
onClick = {
viewModel.login(username, password)
},
// Gradient button
Box(
modifier = Modifier
.fillMaxWidth()
.height(56.dp),
enabled = username.isNotEmpty() && password.isNotEmpty(),
shape = RoundedCornerShape(12.dp)
.height(56.dp)
.clip(MaterialTheme.shapes.medium)
.then(
if (username.isNotEmpty() && password.isNotEmpty() && !isLoading) {
Modifier.background(
Brush.linearGradient(
listOf(
Color(0xFF2563EB),
Color(0xFF8B5CF6)
)
)
)
} else {
Modifier.background(MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.3f))
}
),
contentAlignment = Alignment.Center
) {
if (isLoading) {
CircularProgressIndicator(
modifier = Modifier.size(24.dp),
color = MaterialTheme.colorScheme.onPrimary,
strokeWidth = 2.dp
)
} else {
Text(
"Sign In",
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold
Button(
onClick = {
viewModel.login(username, password)
},
modifier = Modifier.fillMaxSize(),
enabled = username.isNotEmpty() && password.isNotEmpty(),
shape = MaterialTheme.shapes.medium,
colors = ButtonDefaults.buttonColors(
containerColor = Color.Transparent,
disabledContainerColor = Color.Transparent
)
) {
if (isLoading) {
CircularProgressIndicator(
modifier = Modifier.size(24.dp),
color = Color.White,
strokeWidth = 2.dp
)
} else {
Text(
"Sign In",
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold,
color = Color.White
)
}
}
}

View File

@@ -1,9 +1,11 @@
package com.mycrib.android.ui.screens
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
@@ -11,6 +13,9 @@ import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
@@ -315,8 +320,11 @@ fun ResidencesScreen(
modifier = Modifier
.fillMaxWidth()
.clickable { onResidenceClick(residence.id) },
shape = RoundedCornerShape(16.dp),
elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)
shape = MaterialTheme.shapes.large,
elevation = CardDefaults.cardElevation(defaultElevation = 0.dp),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surface
)
) {
Column(
modifier = Modifier
@@ -325,32 +333,67 @@ fun ResidencesScreen(
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
// Gradient circular house icon
Box(
modifier = Modifier
.size(56.dp)
.clip(CircleShape)
.background(
Brush.linearGradient(
listOf(
Color(0xFF2563EB),
Color(0xFF8B5CF6)
)
)
),
contentAlignment = Alignment.Center
) {
Icon(
Icons.Default.Home,
contentDescription = null,
tint = Color.White,
modifier = Modifier.size(28.dp)
)
}
Column(modifier = Modifier.weight(1f)) {
Text(
text = residence.name,
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Bold
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.onSurface
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = "${residence.streetAddress}",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Text(
text = "${residence.city}, ${residence.stateProvince}",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
Icon(
Icons.Default.LocationOn,
contentDescription = null,
modifier = Modifier.size(14.dp),
tint = MaterialTheme.colorScheme.onSurfaceVariant
)
Text(
text = "${residence.city}, ${residence.stateProvince}",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
Icon(
Icons.Default.KeyboardArrowRight,
contentDescription = null,
tint = MaterialTheme.colorScheme.onSurfaceVariant
)
}
Spacer(modifier = Modifier.height(16.dp))
HorizontalDivider()
Spacer(modifier = Modifier.height(12.dp))
HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant)
Spacer(modifier = Modifier.height(16.dp))
Row(
modifier = Modifier.fillMaxWidth(),
@@ -360,19 +403,19 @@ fun ResidencesScreen(
icon = Icons.Default.Assignment,
value = "${residence.taskSummary.total}",
label = "Tasks",
color = MaterialTheme.colorScheme.primary
color = Color(0xFF3B82F6)
)
TaskStatChip(
icon = Icons.Default.CheckCircle,
value = "${residence.taskSummary.completed}",
label = "Done",
color = MaterialTheme.colorScheme.tertiary
color = Color(0xFF10B981)
)
TaskStatChip(
icon = Icons.Default.Schedule,
value = "${residence.taskSummary.pending}",
label = "Pending",
color = MaterialTheme.colorScheme.error
color = Color(0xFFF59E0B)
)
}
}

View File

@@ -0,0 +1,28 @@
package com.mycrib.android.ui.theme
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Shapes
import androidx.compose.ui.unit.dp
// Modern Shape System - Matching iOS Design System
object AppRadius {
val xs = 4.dp
val sm = 8.dp
val md = 12.dp
val lg = 16.dp
val xl = 20.dp
val xxl = 24.dp
}
val AppShapes = Shapes(
// Small components (buttons, chips, badges)
extraSmall = RoundedCornerShape(AppRadius.xs),
small = RoundedCornerShape(AppRadius.sm),
// Medium components (cards, inputs)
medium = RoundedCornerShape(AppRadius.md),
// Large components (dialogs, sheets)
large = RoundedCornerShape(AppRadius.lg),
extraLarge = RoundedCornerShape(AppRadius.xl)
)

View File

@@ -5,51 +5,73 @@ import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
// Bright color palette
private val BrightBlue = Color(0xFF007AFF)
private val BrightGreen = Color(0xFF34C759)
private val BrightOrange = Color(0xFFFF9500)
private val BrightRed = Color(0xFFFF3B30)
private val BrightPurple = Color(0xFFAF52DE)
private val BrightTeal = Color(0xFF5AC8FA)
// Modern Color Palette - Matching iOS Design System
// Primary Colors - Modern blue gradient
private val Primary = Color(0xFF2563EB)
private val PrimaryLight = Color(0xFF3B82F6)
private val PrimaryDark = Color(0xFF1E40AF)
// Light variations for containers
private val LightBlue = Color(0xFFE3F2FD)
private val LightGreen = Color(0xFFE8F5E9)
private val LightOrange = Color(0xFFFFF3E0)
// Accent Colors
private val Accent = Color(0xFF8B5CF6)
private val AccentLight = Color(0xFFA78BFA)
// Dark variations
private val DarkBlue = Color(0xFF0A84FF)
private val DarkGreen = Color(0xFF30D158)
private val DarkOrange = Color(0xFFFF9F0A)
// Semantic Colors
private val Success = Color(0xFF10B981)
private val Warning = Color(0xFFF59E0B)
private val Error = Color(0xFFEF4444)
private val Info = Color(0xFF3B82F6)
// Neutral Colors - Modern grays
private val Background = Color(0xFFF9FAFB)
private val Surface = Color.White
private val SurfaceSecondary = Color(0xFFF3F4F6)
private val TextPrimary = Color(0xFF111827)
private val TextSecondary = Color(0xFF6B7280)
private val TextTertiary = Color(0xFF9CA3AF)
private val Border = Color(0xFFE5E7EB)
private val BorderLight = Color(0xFFF3F4F6)
// Task Status Colors
private val TaskUpcoming = Color(0xFF3B82F6)
private val TaskInProgress = Color(0xFFF59E0B)
private val TaskCompleted = Color(0xFF10B981)
private val TaskCanceled = Color(0xFF6B7280)
private val TaskArchived = Color(0xFF9CA3AF)
// Dark mode variations
private val DarkBackground = Color(0xFF1C1B1F)
private val DarkSurface = Color(0xFF2B2930)
private val DarkSurfaceSecondary = Color(0xFF3B3842)
private val DarkColorScheme = darkColorScheme(
primary = DarkBlue,
primary = PrimaryLight,
onPrimary = Color.White,
primaryContainer = Color(0xFF003D75),
primaryContainer = PrimaryDark,
onPrimaryContainer = Color(0xFFD0E4FF),
secondary = DarkGreen,
secondary = Success,
onSecondary = Color.White,
secondaryContainer = Color(0xFF1B5E20),
onSecondaryContainer = Color(0xFFB9F6CA),
tertiary = DarkOrange,
tertiary = Warning,
onTertiary = Color.White,
tertiaryContainer = Color(0xFF663C00),
onTertiaryContainer = Color(0xFFFFE0B2),
error = BrightRed,
error = Error,
onError = Color.White,
errorContainer = Color(0xFF93000A),
onErrorContainer = Color(0xFFFFDAD6),
background = Color(0xFF1C1B1F),
background = DarkBackground,
onBackground = Color(0xFFE6E1E5),
surface = Color(0xFF1C1B1F),
surface = DarkSurface,
onSurface = Color(0xFFE6E1E5),
surfaceVariant = Color(0xFF49454F),
surfaceVariant = DarkSurfaceSecondary,
onSurfaceVariant = Color(0xFFCAC4D0),
outline = Color(0xFF938F99),
@@ -57,36 +79,36 @@ private val DarkColorScheme = darkColorScheme(
)
private val LightColorScheme = lightColorScheme(
primary = BrightBlue,
primary = Primary,
onPrimary = Color.White,
primaryContainer = LightBlue,
onPrimaryContainer = Color(0xFF001D35),
primaryContainer = Color(0xFFEBF2FF),
onPrimaryContainer = PrimaryDark,
secondary = BrightGreen,
secondary = Success,
onSecondary = Color.White,
secondaryContainer = LightGreen,
onSecondaryContainer = Color(0xFF002106),
secondaryContainer = Color(0xFFD1FAE5),
onSecondaryContainer = Color(0xFF064E3B),
tertiary = BrightOrange,
tertiary = Warning,
onTertiary = Color.White,
tertiaryContainer = LightOrange,
onTertiaryContainer = Color(0xFF2B1700),
tertiaryContainer = Color(0xFFFEF3C7),
onTertiaryContainer = Color(0xFF78350F),
error = BrightRed,
error = Error,
onError = Color.White,
errorContainer = Color(0xFFFFDAD6),
onErrorContainer = Color(0xFF410002),
errorContainer = Color(0xFFFEE2E2),
onErrorContainer = Color(0xFF7F1D1D),
background = Color(0xFFFFFBFE),
onBackground = Color(0xFF1C1B1F),
background = Background,
onBackground = TextPrimary,
surface = Color(0xFFFFFBFE),
onSurface = Color(0xFF1C1B1F),
surfaceVariant = Color(0xFFE7E0EC),
onSurfaceVariant = Color(0xFF49454F),
surface = Surface,
onSurface = TextPrimary,
surfaceVariant = SurfaceSecondary,
onSurfaceVariant = TextSecondary,
outline = Color(0xFF79747E),
outlineVariant = Color(0xFFCAC4D0)
outline = Border,
outlineVariant = BorderLight
)
@Composable
@@ -98,6 +120,20 @@ fun MyCribTheme(
MaterialTheme(
colorScheme = colorScheme,
typography = AppTypography,
shapes = AppShapes,
content = content
)
}
// Extended colors for semantic states (not part of Material3 ColorScheme)
object AppColors {
val accent = Accent
val accentLight = AccentLight
val info = Info
val taskUpcoming = TaskUpcoming
val taskInProgress = TaskInProgress
val taskCompleted = TaskCompleted
val taskCanceled = TaskCanceled
val taskArchived = TaskArchived
}

View File

@@ -0,0 +1,94 @@
package com.mycrib.android.ui.theme
import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
// Modern Typography Scale - Matching iOS Design System
val AppTypography = Typography(
// Display - For hero sections
displayLarge = TextStyle(
fontSize = 57.sp,
fontWeight = FontWeight.Bold,
lineHeight = 64.sp
),
displayMedium = TextStyle(
fontSize = 45.sp,
fontWeight = FontWeight.Bold,
lineHeight = 52.sp
),
displaySmall = TextStyle(
fontSize = 36.sp,
fontWeight = FontWeight.Bold,
lineHeight = 44.sp
),
// Headline - For section headers
headlineLarge = TextStyle(
fontSize = 32.sp,
fontWeight = FontWeight.Bold,
lineHeight = 40.sp
),
headlineMedium = TextStyle(
fontSize = 28.sp,
fontWeight = FontWeight.SemiBold,
lineHeight = 36.sp
),
headlineSmall = TextStyle(
fontSize = 24.sp,
fontWeight = FontWeight.SemiBold,
lineHeight = 32.sp
),
// Title - For card titles
titleLarge = TextStyle(
fontSize = 22.sp,
fontWeight = FontWeight.SemiBold,
lineHeight = 28.sp
),
titleMedium = TextStyle(
fontSize = 18.sp,
fontWeight = FontWeight.SemiBold,
lineHeight = 24.sp
),
titleSmall = TextStyle(
fontSize = 16.sp,
fontWeight = FontWeight.SemiBold,
lineHeight = 20.sp
),
// Body - For main content
bodyLarge = TextStyle(
fontSize = 17.sp,
fontWeight = FontWeight.Normal,
lineHeight = 24.sp
),
bodyMedium = TextStyle(
fontSize = 15.sp,
fontWeight = FontWeight.Normal,
lineHeight = 20.sp
),
bodySmall = TextStyle(
fontSize = 13.sp,
fontWeight = FontWeight.Normal,
lineHeight = 16.sp
),
// Label - For labels and captions
labelLarge = TextStyle(
fontSize = 14.sp,
fontWeight = FontWeight.Medium,
lineHeight = 20.sp
),
labelMedium = TextStyle(
fontSize = 12.sp,
fontWeight = FontWeight.Medium,
lineHeight = 16.sp
),
labelSmall = TextStyle(
fontSize = 11.sp,
fontWeight = FontWeight.Medium,
lineHeight = 16.sp
)
)