1.0.0 - Material you rework

This commit is contained in:
2025-08-31 02:24:26 -06:00
parent e4ea44f766
commit d10622c382
52 changed files with 1689 additions and 975 deletions

View File

@@ -1,17 +1,23 @@
package com.atridad.mealient.ui.components
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.DrawerState
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalDrawerSheet
import androidx.compose.material3.Text
import androidx.compose.material3.Divider
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.atridad.mealient.ui.Dimens
import com.atridad.mealient.ui.R
import com.atridad.mealient.ui.theme.Spacing
import com.atridad.mealient.ui.theme.BorderRadius
interface DrawerItem {
@@ -32,22 +38,47 @@ internal fun DrawerContent(
drawerState: DrawerState,
drawerItems: List<DrawerItem>,
) {
ModalDrawerSheet {
Text(
ModalDrawerSheet(
drawerContainerColor = MaterialTheme.colorScheme.surface,
modifier = Modifier.padding(top = Spacing.lg)
) {
// Header with app branding
androidx.compose.foundation.layout.Column(
modifier = Modifier
.padding(Dimens.Medium),
text = stringResource(id = R.string.menu_navigation_drawer_header),
style = MaterialTheme.typography.headlineSmall,
color = MaterialTheme.colorScheme.primary,
.fillMaxWidth()
.padding(horizontal = Spacing.lg, vertical = Spacing.xl)
) {
androidx.compose.material3.Text(
text = stringResource(id = R.string.menu_navigation_drawer_header),
style = MaterialTheme.typography.headlineMedium,
color = MaterialTheme.colorScheme.primary,
)
androidx.compose.material3.Text(
text = "Your personal recipe collection",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(top = Spacing.xs)
)
}
androidx.compose.material3.HorizontalDivider(
color = MaterialTheme.colorScheme.outlineVariant,
thickness = 1.dp,
modifier = Modifier.padding(horizontal = Spacing.lg)
)
drawerItems.forEach { item ->
NavigationDrawerItem(
name = item.getName(),
selected = item.isSelected(),
icon = item.icon,
onClick = { item.onClick(drawerState) },
)
// Navigation items
androidx.compose.foundation.layout.Column(
modifier = Modifier.padding(vertical = Spacing.md)
) {
drawerItems.forEach { item ->
NavigationDrawerItem(
name = item.getName(),
selected = item.isSelected(),
icon = item.icon,
onClick = { item.onClick(drawerState) },
)
}
}
}
}
@@ -62,10 +93,12 @@ private fun NavigationDrawerItem(
) {
androidx.compose.material3.NavigationDrawerItem(
modifier = modifier
.padding(horizontal = Dimens.Medium),
.padding(horizontal = Spacing.md),
label = {
Text(
text = name,
style = MaterialTheme.typography.bodyLarge,
color = if (selected) MaterialTheme.colorScheme.onPrimaryContainer else MaterialTheme.colorScheme.onSurface
)
},
selected = selected,
@@ -73,8 +106,18 @@ private fun NavigationDrawerItem(
Icon(
imageVector = icon,
contentDescription = null,
tint = if (selected) MaterialTheme.colorScheme.onPrimaryContainer else MaterialTheme.colorScheme.onSurfaceVariant
)
},
onClick = onClick,
colors = androidx.compose.material3.NavigationDrawerItemDefaults.colors(
selectedContainerColor = MaterialTheme.colorScheme.primaryContainer,
unselectedContainerColor = androidx.compose.ui.graphics.Color.Transparent,
selectedIconColor = MaterialTheme.colorScheme.onPrimaryContainer,
unselectedIconColor = MaterialTheme.colorScheme.onSurfaceVariant,
selectedTextColor = MaterialTheme.colorScheme.onPrimaryContainer,
unselectedTextColor = MaterialTheme.colorScheme.onSurface
),
shape = androidx.compose.foundation.shape.RoundedCornerShape(BorderRadius.md)
)
}

View File

@@ -0,0 +1,80 @@
package com.atridad.mealient.ui.theme
import androidx.compose.ui.graphics.Color
// Light Theme Colors
val md_theme_light_primary = Color(0xFF6750A4)
val md_theme_light_onPrimary = Color(0xFFFFFFFF)
val md_theme_light_primaryContainer = Color(0xFFEADDFF)
val md_theme_light_onPrimaryContainer = Color(0xFF21005D)
val md_theme_light_secondary = Color(0xFF625B71)
val md_theme_light_onSecondary = Color(0xFFFFFFFF)
val md_theme_light_secondaryContainer = Color(0xFFE8DEF8)
val md_theme_light_onSecondaryContainer = Color(0xFF1D192B)
val md_theme_light_tertiary = Color(0xFF7D5260)
val md_theme_light_onTertiary = Color(0xFFFFFFFF)
val md_theme_light_tertiaryContainer = Color(0xFFFFD8E4)
val md_theme_light_onTertiaryContainer = Color(0xFF31111D)
val md_theme_light_error = Color(0xFFBA1A1A)
val md_theme_light_errorContainer = Color(0xFFFFDAD6)
val md_theme_light_onError = Color(0xFFFFFFFF)
val md_theme_light_onErrorContainer = Color(0xFF410002)
val md_theme_light_background = Color(0xFFFFFBFE)
val md_theme_light_onBackground = Color(0xFF1C1B1F)
val md_theme_light_surface = Color(0xFFFFFBFE)
val md_theme_light_onSurface = Color(0xFF1C1B1F)
val md_theme_light_surfaceVariant = Color(0xFFE7E0EC)
val md_theme_light_onSurfaceVariant = Color(0xFF49454F)
val md_theme_light_outline = Color(0xFF79747E)
val md_theme_light_inverseOnSurface = Color(0xFFF4EFF4)
val md_theme_light_inverseSurface = Color(0xFF313033)
val md_theme_light_inversePrimary = Color(0xFFD0BCFF)
val md_theme_light_shadow = Color(0xFF000000)
val md_theme_light_surfaceTint = Color(0xFF6750A4)
val md_theme_light_outlineVariant = Color(0xFFCAC4D0)
val md_theme_light_scrim = Color(0xFF000000)
// Dark Theme Colors
val md_theme_dark_primary = Color(0xFFD0BCFF)
val md_theme_dark_onPrimary = Color(0xFF381E72)
val md_theme_dark_primaryContainer = Color(0xFF4F378B)
val md_theme_dark_onPrimaryContainer = Color(0xFFEADDFF)
val md_theme_dark_secondary = Color(0xFFCCC2DC)
val md_theme_dark_onSecondary = Color(0xFF332D41)
val md_theme_dark_secondaryContainer = Color(0xFF4A4458)
val md_theme_dark_onSecondaryContainer = Color(0xFFE8DEF8)
val md_theme_dark_tertiary = Color(0xFFEFB8C8)
val md_theme_dark_onTertiary = Color(0xFF492532)
val md_theme_dark_tertiaryContainer = Color(0xFF633B48)
val md_theme_dark_onTertiaryContainer = Color(0xFFFFD8E4)
val md_theme_dark_error = Color(0xFFFFB4AB)
val md_theme_dark_errorContainer = Color(0xFF93000A)
val md_theme_dark_onError = Color(0xFF690005)
val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6)
val md_theme_dark_background = Color(0xFF1C1B1F)
val md_theme_dark_onBackground = Color(0xFFE6E1E5)
val md_theme_dark_surface = Color(0xFF1C1B1F)
val md_theme_dark_onSurface = Color(0xFFE6E1E5)
val md_theme_dark_surfaceVariant = Color(0xFF49454F)
val md_theme_dark_onSurfaceVariant = Color(0xFFCAC4D0)
val md_theme_dark_outline = Color(0xFF938F99)
val md_theme_dark_inverseOnSurface = Color(0xFF1C1B1F)
val md_theme_dark_inverseSurface = Color(0xFFE6E1E5)
val md_theme_dark_inversePrimary = Color(0xFF6750A4)
val md_theme_dark_shadow = Color(0xFF000000)
val md_theme_dark_surfaceTint = Color(0xFFD0BCFF)
val md_theme_dark_outlineVariant = Color(0xFF49454F)
val md_theme_dark_scrim = Color(0xFF000000)
// Custom App Colors
val md_theme_light_food_primary = Color(0xFF2E7D32) // Green for food
val md_theme_light_food_secondary = Color(0xFFFF8F00) // Orange for recipes
val md_theme_light_food_tertiary = Color(0xFFD32F2F) // Red for cooking
val md_theme_light_food_surface = Color(0xFFF8F9FA)
val md_theme_light_food_surfaceVariant = Color(0xFFE8F5E8)
val md_theme_dark_food_primary = Color(0xFF4CAF50) // Green for food
val md_theme_dark_food_secondary = Color(0xFFFFB74D) // Orange for recipes
val md_theme_dark_food_tertiary = Color(0xFFEF5350) // Red for cooking
val md_theme_dark_food_surface = Color(0xFF1A1A1A)
val md_theme_dark_food_surfaceVariant = Color(0xFF2E2E2E)

View File

@@ -0,0 +1,83 @@
package com.atridad.mealient.ui.theme
import androidx.compose.ui.unit.dp
// Material 3 Spacing System
object Spacing {
// Base spacing unit is 4dp
val xs = 4.dp
val sm = 8.dp
val md = 16.dp
val lg = 24.dp
val xl = 32.dp
val xxl = 48.dp
val xxxl = 64.dp
}
// Material 3 Component Sizing
object ComponentSizing {
// Button heights
val buttonHeight = 40.dp
val buttonHeightLarge = 48.dp
val buttonHeightSmall = 32.dp
// Input field heights
val inputFieldHeight = 56.dp
val inputFieldHeightSmall = 48.dp
// Card dimensions
val cardElevation = 1.dp
val cardElevationHovered = 8.dp
val cardElevationPressed = 12.dp
// Icon sizes
val iconSize = 24.dp
val iconSizeSmall = 20.dp
val iconSizeLarge = 32.dp
// Avatar sizes
val avatarSize = 40.dp
val avatarSizeSmall = 32.dp
val avatarSizeLarge = 56.dp
// FAB sizes
val fabSize = 56.dp
val fabSizeSmall = 40.dp
val fabSizeLarge = 96.dp
}
// Material 3 Border Radius
object BorderRadius {
val xs = 4.dp
val sm = 8.dp
val md = 12.dp
val lg = 16.dp
val xl = 20.dp
val xxl = 28.dp
val full = 50.dp
}
// Material 3 Content Padding
object ContentPadding {
val xs = 4.dp
val sm = 8.dp
val md = 16.dp
val lg = 24.dp
val xl = 32.dp
val xxl = 48.dp
}
// Material 3 List Item Spacing
object ListSpacing {
val itemSpacing = 8.dp
val sectionSpacing = 16.dp
val listPadding = 16.dp
}
// Material 3 Screen Margins
object ScreenMargins {
val horizontal = 16.dp
val horizontalLarge = 24.dp
val vertical = 16.dp
val verticalLarge = 24.dp
}

View File

@@ -0,0 +1,144 @@
package com.atridad.mealient.ui.theme
import android.app.Activity
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat
private val LightColorScheme = lightColorScheme(
// Primary colors
primary = md_theme_light_primary,
onPrimary = md_theme_light_onPrimary,
primaryContainer = md_theme_light_primaryContainer,
onPrimaryContainer = md_theme_light_onPrimaryContainer,
// Secondary colors
secondary = md_theme_light_secondary,
onSecondary = md_theme_light_onSecondary,
secondaryContainer = md_theme_light_secondaryContainer,
onSecondaryContainer = md_theme_light_onSecondaryContainer,
// Tertiary colors
tertiary = md_theme_light_tertiary,
onTertiary = md_theme_light_onTertiary,
tertiaryContainer = md_theme_light_tertiaryContainer,
onTertiaryContainer = md_theme_light_onTertiaryContainer,
// Error colors
error = md_theme_light_error,
onError = md_theme_light_onError,
errorContainer = md_theme_light_errorContainer,
onErrorContainer = md_theme_light_onErrorContainer,
// Neutral colors
background = md_theme_light_background,
onBackground = md_theme_light_onBackground,
surface = md_theme_light_surface,
onSurface = md_theme_light_onSurface,
surfaceVariant = md_theme_light_surfaceVariant,
onSurfaceVariant = md_theme_light_onSurfaceVariant,
// Outline colors
outline = md_theme_light_outline,
outlineVariant = md_theme_light_outlineVariant,
// Inverse colors
inverseOnSurface = md_theme_light_inverseOnSurface,
inverseSurface = md_theme_light_inverseSurface,
inversePrimary = md_theme_light_inversePrimary,
// Surface colors
surfaceTint = md_theme_light_surfaceTint,
scrim = md_theme_light_scrim,
)
private val DarkColorScheme = darkColorScheme(
// Primary colors
primary = md_theme_dark_primary,
onPrimary = md_theme_dark_onPrimary,
primaryContainer = md_theme_dark_primaryContainer,
onPrimaryContainer = md_theme_dark_onPrimaryContainer,
// Secondary colors
secondary = md_theme_dark_secondary,
onSecondary = md_theme_dark_onSecondary,
secondaryContainer = md_theme_dark_secondaryContainer,
onSecondaryContainer = md_theme_dark_onSecondaryContainer,
// Tertiary colors
tertiary = md_theme_dark_tertiary,
onTertiary = md_theme_dark_onTertiary,
tertiaryContainer = md_theme_dark_tertiaryContainer,
onTertiaryContainer = md_theme_dark_onTertiaryContainer,
// Error colors
error = md_theme_dark_error,
onError = md_theme_dark_onError,
errorContainer = md_theme_dark_errorContainer,
onErrorContainer = md_theme_dark_onErrorContainer,
// Neutral colors
background = md_theme_dark_background,
onBackground = md_theme_dark_onBackground,
surface = md_theme_dark_surface,
onSurface = md_theme_dark_onSurface,
surfaceVariant = md_theme_dark_surfaceVariant,
onSurfaceVariant = md_theme_dark_onSurfaceVariant,
// Outline colors
outline = md_theme_dark_outline,
outlineVariant = md_theme_dark_outlineVariant,
// Inverse colors
inverseOnSurface = md_theme_dark_inverseOnSurface,
inverseSurface = md_theme_dark_inverseSurface,
inversePrimary = md_theme_dark_inversePrimary,
// Surface colors
surfaceTint = md_theme_dark_surfaceTint,
scrim = md_theme_dark_scrim,
)
@Composable
fun MealientTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColorScheme
else -> LightColorScheme
}
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
val window = (view.context as Activity).window
// Make status bar transparent so it blends with the background
window.statusBarColor = android.graphics.Color.TRANSPARENT
// Set light status bar icons for dark theme, dark icons for light theme
// This ensures proper contrast for the status bar icons
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = !darkTheme
}
}
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
)
}

View File

@@ -0,0 +1,125 @@
package com.atridad.mealient.ui.theme
import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
// Material 3 Typography Scale
val Typography = Typography(
// Display styles - Large text for hero sections
displayLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 57.sp,
lineHeight = 64.sp,
letterSpacing = (-0.25).sp,
),
displayMedium = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 45.sp,
lineHeight = 52.sp,
letterSpacing = 0.sp,
),
displaySmall = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 36.sp,
lineHeight = 44.sp,
letterSpacing = 0.sp,
),
// Headline styles - Section titles
headlineLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 32.sp,
lineHeight = 40.sp,
letterSpacing = 0.sp,
),
headlineMedium = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 28.sp,
lineHeight = 36.sp,
letterSpacing = 0.sp,
),
headlineSmall = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 24.sp,
lineHeight = 32.sp,
letterSpacing = 0.sp,
),
// Title styles - Card titles, list headers
titleLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Medium,
fontSize = 22.sp,
lineHeight = 28.sp,
letterSpacing = 0.sp,
),
titleMedium = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Medium,
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.15.sp,
),
titleSmall = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Medium,
fontSize = 14.sp,
lineHeight = 20.sp,
letterSpacing = 0.1.sp,
),
// Body styles - Main content text
bodyLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.5.sp,
),
bodyMedium = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 14.sp,
lineHeight = 20.sp,
letterSpacing = 0.25.sp,
),
bodySmall = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 12.sp,
lineHeight = 16.sp,
letterSpacing = 0.4.sp,
),
// Label styles - Buttons, form fields, captions
labelLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Medium,
fontSize = 14.sp,
lineHeight = 20.sp,
letterSpacing = 0.1.sp,
),
labelMedium = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Medium,
fontSize = 12.sp,
lineHeight = 16.sp,
letterSpacing = 0.5.sp,
),
labelSmall = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Medium,
fontSize = 11.sp,
lineHeight = 16.sp,
letterSpacing = 0.5.sp,
),
)