refactor: CloserPrimitives design system, PairPromptScreen, state component overhauls (batch v0.2.1)

- Add CloserPrimitives (CloserCard, CloserActionButton, CloserSpacing, CloserButtonStyle, SkeletonLine)
- Refactor EmptyState, ErrorState, LoadingState to use primitives with closer design tokens
- Add PairPromptScreen + nav route for post-signup partner invitation
- Update SignUpScreen onboarding copy to mention partner invite flow
- HomeScreen and PlayHubScreen layout/structure refinements
This commit is contained in:
null 2026-06-19 04:04:52 -05:00
parent 164e0e47ca
commit 79a63629f1
11 changed files with 644 additions and 299 deletions

View File

@ -42,6 +42,7 @@ import app.closer.ui.pairing.AcceptInviteScreen
import app.closer.ui.pairing.CreateInviteScreen import app.closer.ui.pairing.CreateInviteScreen
import app.closer.ui.pairing.EmailInviteScreen import app.closer.ui.pairing.EmailInviteScreen
import app.closer.ui.pairing.InviteConfirmScreen import app.closer.ui.pairing.InviteConfirmScreen
import app.closer.ui.pairing.PairPromptScreen
import app.closer.ui.dates.DateMatchScreen import app.closer.ui.dates.DateMatchScreen
import app.closer.ui.dates.DateMatchesScreen import app.closer.ui.dates.DateMatchesScreen
import app.closer.ui.dates.DateBuilderScreen import app.closer.ui.dates.DateBuilderScreen
@ -161,6 +162,9 @@ fun AppNavigation(
composable(route = AppRoute.CREATE_PROFILE) { composable(route = AppRoute.CREATE_PROFILE) {
CreateProfileScreen(onNavigate = navigateRoute) CreateProfileScreen(onNavigate = navigateRoute)
} }
composable(route = AppRoute.PAIR_PROMPT) {
PairPromptScreen(onNavigate = navigateRoute)
}
// Auth // Auth
composable(route = AppRoute.LOGIN) { composable(route = AppRoute.LOGIN) {

View File

@ -8,6 +8,7 @@ object AppRoute {
const val SIGN_UP = "sign_up" const val SIGN_UP = "sign_up"
const val FORGOT_PASSWORD = "forgot_password" const val FORGOT_PASSWORD = "forgot_password"
const val CREATE_PROFILE = "create_profile" const val CREATE_PROFILE = "create_profile"
const val PAIR_PROMPT = "pair_prompt"
const val HOME = "home" const val HOME = "home"
const val PARTNER_HOME = "partner_home" const val PARTNER_HOME = "partner_home"
const val PLAY = "play" const val PLAY = "play"
@ -62,6 +63,7 @@ object AppRoute {
val definitions = listOf( val definitions = listOf(
Definition(ONBOARDING, "Onboarding", "onboarding"), Definition(ONBOARDING, "Onboarding", "onboarding"),
Definition(CREATE_PROFILE, "Create Profile", "onboarding"), Definition(CREATE_PROFILE, "Create Profile", "onboarding"),
Definition(PAIR_PROMPT, "Invite Partner", "onboarding"),
Definition(LOGIN, "Login", "auth"), Definition(LOGIN, "Login", "auth"),
Definition(SIGN_UP, "Sign Up", "auth"), Definition(SIGN_UP, "Sign Up", "auth"),
Definition(FORGOT_PASSWORD, "Forgot Password", "auth"), Definition(FORGOT_PASSWORD, "Forgot Password", "auth"),

View File

@ -110,7 +110,7 @@ fun SignUpScreen(
) )
Spacer(Modifier.height(8.dp)) Spacer(Modifier.height(8.dp))
Text( Text(
text = "You'll set your name after signing up.", text = "Takes about a minute. You'll set your name and invite your partner right after.",
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
color = AuthMuted, color = AuthMuted,
textAlign = TextAlign.Center textAlign = TextAlign.Center

View File

@ -0,0 +1,224 @@
package app.closer.ui.components
import app.closer.ui.theme.CloserPalette
import app.closer.ui.theme.closerCardColor
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
object CloserSpacing {
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 Xxxl = 32.dp
}
object CloserRadii {
val Button = 16.dp
val Card = 24.dp
val FeatureCard = 30.dp
val Pill = 999.dp
val Tile = 18.dp
}
object CloserElevations {
val Flat = 0.dp
val Card = 4.dp
val Feature = 10.dp
}
enum class CloserButtonStyle {
Primary,
Secondary,
Destructive
}
@Composable
fun CloserCard(
modifier: Modifier = Modifier,
containerColor: Color = closerCardColor(alpha = 0.9f),
contentColor: Color = MaterialTheme.colorScheme.onSurface,
shape: RoundedCornerShape = RoundedCornerShape(CloserRadii.Card),
elevation: Dp = CloserElevations.Card,
content: @Composable () -> Unit
) {
Card(
modifier = modifier,
shape = shape,
colors = CardDefaults.cardColors(
containerColor = containerColor,
contentColor = contentColor
),
elevation = CardDefaults.cardElevation(defaultElevation = elevation)
) {
content()
}
}
@Composable
fun CloserClickableCard(
onClick: () -> Unit,
modifier: Modifier = Modifier,
containerColor: Color = closerCardColor(alpha = 0.9f),
contentColor: Color = MaterialTheme.colorScheme.onSurface,
shape: RoundedCornerShape = RoundedCornerShape(CloserRadii.Card),
elevation: Dp = CloserElevations.Card,
content: @Composable () -> Unit
) {
Card(
onClick = onClick,
modifier = modifier,
shape = shape,
colors = CardDefaults.cardColors(
containerColor = containerColor,
contentColor = contentColor
),
elevation = CardDefaults.cardElevation(defaultElevation = elevation)
) {
content()
}
}
@Composable
fun CloserActionButton(
label: String,
onClick: () -> Unit,
modifier: Modifier = Modifier,
style: CloserButtonStyle = CloserButtonStyle.Primary,
enabled: Boolean = true,
containerColor: Color? = null,
contentColor: Color? = null
) {
val shape = RoundedCornerShape(CloserRadii.Button)
when (style) {
CloserButtonStyle.Primary -> Button(
onClick = onClick,
modifier = modifier,
enabled = enabled,
shape = shape,
colors = ButtonDefaults.buttonColors(
containerColor = containerColor ?: MaterialTheme.colorScheme.primary,
contentColor = contentColor ?: MaterialTheme.colorScheme.onPrimary
)
) {
Text(label)
}
CloserButtonStyle.Secondary -> OutlinedButton(
onClick = onClick,
modifier = modifier,
enabled = enabled,
shape = shape,
border = BorderStroke(1.dp, MaterialTheme.colorScheme.primary.copy(alpha = 0.34f)),
colors = ButtonDefaults.outlinedButtonColors(
containerColor = containerColor ?: MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.42f),
contentColor = contentColor ?: CloserPalette.PurpleDeep
)
) {
Text(label)
}
CloserButtonStyle.Destructive -> OutlinedButton(
onClick = onClick,
modifier = modifier,
enabled = enabled,
shape = shape,
border = BorderStroke(1.dp, CloserPalette.Danger.copy(alpha = 0.38f)),
colors = ButtonDefaults.outlinedButtonColors(
containerColor = containerColor ?: CloserPalette.PinkMist,
contentColor = contentColor ?: CloserPalette.Danger
)
) {
Text(label)
}
}
}
@Composable
fun CloserPill(
label: String,
modifier: Modifier = Modifier,
containerColor: Color = MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.55f),
contentColor: Color = CloserPalette.PurpleDeep
) {
Surface(
modifier = modifier,
shape = RoundedCornerShape(CloserRadii.Pill),
color = containerColor,
contentColor = contentColor
) {
Text(
text = label,
modifier = Modifier.padding(horizontal = CloserSpacing.Md, vertical = CloserSpacing.Xs),
style = MaterialTheme.typography.labelMedium,
fontWeight = FontWeight.SemiBold
)
}
}
@Composable
fun CloserIconTile(
modifier: Modifier = Modifier,
containerColor: Color = MaterialTheme.colorScheme.primaryContainer,
contentColor: Color = MaterialTheme.colorScheme.primary,
content: @Composable RowScope.() -> Unit
) {
Surface(
modifier = modifier,
shape = RoundedCornerShape(CloserRadii.Tile),
color = containerColor,
contentColor = contentColor
) {
Row(
modifier = Modifier.padding(CloserSpacing.Md),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
content = content
)
}
}
@Composable
fun CloserSectionHeader(
title: String,
modifier: Modifier = Modifier,
action: (@Composable () -> Unit)? = null
) {
Row(
modifier = modifier,
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = title,
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold,
color = MaterialTheme.colorScheme.onSurface
)
Box {
action?.invoke()
}
}
}

View File

@ -5,18 +5,11 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
@Composable @Composable
fun EmptyState( fun EmptyState(
@ -26,14 +19,13 @@ fun EmptyState(
onAction: (() -> Unit)? = null, onAction: (() -> Unit)? = null,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
Card( CloserCard(
modifier = modifier.fillMaxWidth(), modifier = modifier.fillMaxWidth(),
shape = RoundedCornerShape(28.dp), containerColor = closerCardColor(alpha = 0.86f)
colors = CardDefaults.cardColors(containerColor = closerCardColor(alpha = 0.86f))
) { ) {
Column( Column(
modifier = Modifier.padding(20.dp), modifier = Modifier.padding(CloserSpacing.Xl),
verticalArrangement = Arrangement.spacedBy(12.dp) verticalArrangement = Arrangement.spacedBy(CloserSpacing.Md)
) { ) {
Text( Text(
text = title, text = title,
@ -47,17 +39,11 @@ fun EmptyState(
color = MaterialTheme.colorScheme.onSurfaceVariant color = MaterialTheme.colorScheme.onSurfaceVariant
) )
if (actionLabel != null && onAction != null) { if (actionLabel != null && onAction != null) {
Button( CloserActionButton(
label = actionLabel,
onClick = onAction, onClick = onAction,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth()
shape = RoundedCornerShape(16.dp),
colors = ButtonDefaults.buttonColors(
containerColor = Color(0xFFB98AF4),
contentColor = MaterialTheme.colorScheme.onPrimary
) )
) {
Text(actionLabel)
}
} }
} }
} }

View File

@ -1,20 +1,15 @@
package app.closer.ui.components package app.closer.ui.components
import app.closer.ui.theme.CloserPalette
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
@Composable @Composable
fun ErrorState( fun ErrorState(
@ -24,20 +19,19 @@ fun ErrorState(
onRetry: (() -> Unit)? = null, onRetry: (() -> Unit)? = null,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
Card( CloserCard(
modifier = modifier.fillMaxWidth(), modifier = modifier.fillMaxWidth(),
shape = RoundedCornerShape(28.dp), containerColor = CloserPalette.PinkMist
colors = CardDefaults.cardColors(containerColor = Color(0xFFFFEEF7))
) { ) {
Column( Column(
modifier = Modifier.padding(22.dp), modifier = Modifier.padding(CloserSpacing.Xxl),
verticalArrangement = Arrangement.spacedBy(10.dp) verticalArrangement = Arrangement.spacedBy(CloserSpacing.Md)
) { ) {
Text( Text(
text = title, text = title,
style = MaterialTheme.typography.titleMedium, style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold, fontWeight = FontWeight.SemiBold,
color = Color(0xFF8D2D35) color = CloserPalette.Danger
) )
Text( Text(
text = message, text = message,
@ -45,13 +39,12 @@ fun ErrorState(
color = MaterialTheme.colorScheme.onSurfaceVariant color = MaterialTheme.colorScheme.onSurfaceVariant
) )
if (onRetry != null) { if (onRetry != null) {
OutlinedButton( CloserActionButton(
label = retryLabel,
onClick = onRetry, onClick = onRetry,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(14.dp) style = CloserButtonStyle.Destructive
) { )
Text(retryLabel)
}
} }
} }
} }

View File

@ -1,20 +1,28 @@
package app.closer.ui.components package app.closer.ui.components
import app.closer.ui.theme.CloserPalette
import app.closer.ui.theme.closerCardColor
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@ -23,21 +31,32 @@ fun LoadingState(
message: String = "Loading…", message: String = "Loading…",
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
Card( val shimmer = closerSkeletonBrush()
CloserCard(
modifier = modifier.fillMaxWidth(), modifier = modifier.fillMaxWidth(),
shape = RoundedCornerShape(28.dp), containerColor = closerCardColor(alpha = 0.8f)
colors = CardDefaults.cardColors(containerColor = Color.White.copy(alpha = 0.8f))
) { ) {
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(32.dp), .padding(CloserSpacing.Xxxl),
horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(CloserSpacing.Md)
verticalArrangement = Arrangement.spacedBy(16.dp)
) { ) {
CircularProgressIndicator( SkeletonLine(
modifier = Modifier.size(34.dp), brush = shimmer,
color = Color(0xFFB98AF4) modifier = Modifier.fillMaxWidth(0.42f),
height = 18.dp
)
SkeletonLine(
brush = shimmer,
modifier = Modifier.fillMaxWidth(),
height = 13.dp
)
SkeletonLine(
brush = shimmer,
modifier = Modifier.fillMaxWidth(0.72f),
height = 13.dp
) )
Text( Text(
text = message, text = message,
@ -48,3 +67,41 @@ fun LoadingState(
} }
} }
} }
@Composable
private fun closerSkeletonBrush(): Brush {
val transition = rememberInfiniteTransition(label = "closerSkeleton")
val shimmerOffset = transition.animateFloat(
initialValue = -320f,
targetValue = 640f,
animationSpec = infiniteRepeatable(
animation = tween(durationMillis = 1200, easing = LinearEasing),
repeatMode = RepeatMode.Restart
),
label = "closerSkeletonOffset"
)
return Brush.linearGradient(
colors = listOf(
CloserPalette.PurpleMist.copy(alpha = 0.55f),
CloserPalette.PurpleSoft.copy(alpha = 0.95f),
CloserPalette.PinkMist.copy(alpha = 0.72f)
),
start = Offset(shimmerOffset.value, 0f),
end = Offset(shimmerOffset.value + 280f, 0f)
)
}
@Composable
private fun SkeletonLine(
brush: Brush,
modifier: Modifier = Modifier,
height: androidx.compose.ui.unit.Dp
) {
Box(
modifier = modifier
.height(height)
.clip(RoundedCornerShape(CloserRadii.Pill))
.background(brush)
)
}

View File

@ -1,7 +1,22 @@
package app.closer.ui.home package app.closer.ui.home
import app.closer.ui.theme.closerCardColor import app.closer.core.navigation.AppRoute
import app.closer.domain.model.Question
import app.closer.domain.model.QuestionCategory
import app.closer.ui.components.CategoryGlyph
import app.closer.ui.components.CloserActionButton
import app.closer.ui.components.CloserButtonStyle
import app.closer.ui.components.CloserCard
import app.closer.ui.components.CloserClickableCard
import app.closer.ui.components.CloserElevations
import app.closer.ui.components.CloserPill
import app.closer.ui.components.CloserRadii
import app.closer.ui.components.ErrorState
import app.closer.ui.components.LoadingState
import app.closer.ui.questions.displayCategoryName
import app.closer.ui.theme.CloserPalette
import app.closer.ui.theme.closerBackgroundBrush import app.closer.ui.theme.closerBackgroundBrush
import app.closer.ui.theme.closerCardColor
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
@ -19,19 +34,15 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowForward import androidx.compose.material.icons.automirrored.filled.ArrowForward
import androidx.compose.material.icons.filled.Favorite import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Offset
@ -41,16 +52,9 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.LaunchedEffect import androidx.hilt.navigation.compose.hiltViewModel
import androidx.compose.runtime.remember
import app.closer.core.navigation.AppRoute
import app.closer.domain.model.Question
import app.closer.domain.model.QuestionCategory
import app.closer.ui.components.CategoryGlyph
import app.closer.ui.questions.displayCategoryName
@Composable @Composable
fun HomeScreen( fun HomeScreen(
@ -211,17 +215,17 @@ private fun PrimaryHomeActionCard(
) { ) {
val colors = action.tone.actionColors() val colors = action.tone.actionColors()
Card( CloserCard(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(32.dp), shape = RoundedCornerShape(CloserRadii.FeatureCard),
colors = CardDefaults.cardColors(containerColor = closerCardColor(alpha = 0.92f)), containerColor = closerCardColor(alpha = 0.92f),
elevation = CardDefaults.cardElevation(defaultElevation = 18.dp) elevation = CloserElevations.Feature
) { ) {
Column( Column(
modifier = Modifier modifier = Modifier
.background( .background(
Brush.linearGradient( Brush.linearGradient(
listOf(Color.White, colors.soft, Color(0xFFFFF7FB)), listOf(MaterialTheme.colorScheme.surface, colors.soft, CloserPalette.PinkMist.copy(alpha = 0.72f)),
start = Offset.Zero, start = Offset.Zero,
end = Offset.Infinite end = Offset.Infinite
) )
@ -243,7 +247,7 @@ private fun PrimaryHomeActionCard(
verticalAlignment = Alignment.Top verticalAlignment = Alignment.Top
) { ) {
Surface( Surface(
shape = RoundedCornerShape(20.dp), shape = RoundedCornerShape(CloserRadii.Tile),
color = colors.accent.copy(alpha = 0.16f), color = colors.accent.copy(alpha = 0.16f),
modifier = Modifier.size(52.dp) modifier = Modifier.size(52.dp)
) { ) {
@ -280,17 +284,13 @@ private fun PrimaryHomeActionCard(
HomePulseStrip(stats = stats, streakCount = streakCount) HomePulseStrip(stats = stats, streakCount = streakCount)
Button( CloserActionButton(
label = action.cta,
onClick = { onAction(action) }, onClick = { onAction(action) },
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(18.dp), containerColor = colors.accent,
colors = ButtonDefaults.buttonColors( contentColor = colors.onAccent
containerColor = colors.accent, )
contentColor = colors.onAccent
)
) {
Text(action.cta)
}
} }
} }
} }
@ -330,8 +330,8 @@ private fun PulseMetric(
) { ) {
Surface( Surface(
modifier = modifier, modifier = modifier,
shape = RoundedCornerShape(18.dp), shape = RoundedCornerShape(CloserRadii.Tile),
color = Color.White.copy(alpha = 0.66f) color = MaterialTheme.colorScheme.surface.copy(alpha = 0.66f)
) { ) {
Column( Column(
modifier = Modifier.padding(horizontal = 12.dp, vertical = 10.dp), modifier = Modifier.padding(horizontal = 12.dp, vertical = 10.dp),
@ -340,7 +340,7 @@ private fun PulseMetric(
Text( Text(
text = value, text = value,
style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.SemiBold), style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.SemiBold),
color = Color(0xFF56336F), color = CloserPalette.PurpleDeep,
maxLines = 1 maxLines = 1
) )
Text( Text(
@ -365,7 +365,7 @@ private fun ActionFeedSection(
Text( Text(
text = "After that", text = "After that",
style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.SemiBold), style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.SemiBold),
color = Color(0xFF2C2233) color = MaterialTheme.colorScheme.onSurface
) )
actions.forEach { action -> actions.forEach { action ->
SecondaryHomeActionCard(action = action, onAction = onAction) SecondaryHomeActionCard(action = action, onAction = onAction)
@ -380,12 +380,12 @@ private fun SecondaryHomeActionCard(
) { ) {
val colors = action.tone.actionColors() val colors = action.tone.actionColors()
Card( CloserClickableCard(
onClick = { onAction(action) }, onClick = { onAction(action) },
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(24.dp), shape = RoundedCornerShape(CloserRadii.Card),
colors = CardDefaults.cardColors(containerColor = Color.White), containerColor = MaterialTheme.colorScheme.surface,
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp) elevation = CloserElevations.Card
) { ) {
Row( Row(
modifier = Modifier modifier = Modifier
@ -397,7 +397,7 @@ private fun SecondaryHomeActionCard(
Box( Box(
modifier = Modifier modifier = Modifier
.size(40.dp) .size(40.dp)
.background(colors.soft, RoundedCornerShape(15.dp)), .background(colors.soft, RoundedCornerShape(CloserRadii.Button)),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
Icon( Icon(
@ -435,7 +435,7 @@ private fun SecondaryHomeActionCard(
) )
} }
Surface( Surface(
shape = RoundedCornerShape(14.dp), shape = RoundedCornerShape(CloserRadii.Button),
color = colors.soft color = colors.soft
) { ) {
Icon( Icon(
@ -453,10 +453,11 @@ private fun SecondaryHomeActionCard(
@Composable @Composable
private fun MomentCueCard() { private fun MomentCueCard() {
Surface( CloserCard(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(24.dp), shape = RoundedCornerShape(CloserRadii.Card),
color = Color(0xFFFFF8FC) containerColor = CloserPalette.PinkMist.copy(alpha = 0.7f),
elevation = CloserElevations.Flat
) { ) {
Column( Column(
modifier = Modifier.padding(17.dp), modifier = Modifier.padding(17.dp),
@ -489,42 +490,43 @@ private data class HomeActionColors(
val onAccent: Color = Color(0xFF24122F) val onAccent: Color = Color(0xFF24122F)
) )
@Composable
private fun HomeActionTone.actionColors(): HomeActionColors = private fun HomeActionTone.actionColors(): HomeActionColors =
when (this) { when (this) {
HomeActionTone.Invite -> HomeActionColors( HomeActionTone.Invite -> HomeActionColors(
soft = Color(0xFFF4E8FF), soft = CloserPalette.PurpleSoft,
accent = Color(0xFFB98AF4), accent = MaterialTheme.colorScheme.primary,
deep = Color(0xFF56306F) deep = CloserPalette.PurpleDeep
) )
HomeActionTone.Daily -> HomeActionColors( HomeActionTone.Daily -> HomeActionColors(
soft = Color(0xFFFFE9F4), soft = CloserPalette.PinkSoft,
accent = Color(0xFFE7A2D1), accent = CloserPalette.PinkBright,
deep = Color(0xFF6D2B55) deep = CloserPalette.PinkAccentDeep
) )
HomeActionTone.Reflection -> HomeActionColors( HomeActionTone.Reflection -> HomeActionColors(
soft = Color(0xFFF6E8FF), soft = CloserPalette.PurpleGlow,
accent = Color(0xFFC89AF2), accent = CloserPalette.PurpleRich,
deep = Color(0xFF56306F) deep = CloserPalette.PurpleDeep
) )
HomeActionTone.Ritual -> HomeActionColors( HomeActionTone.Ritual -> HomeActionColors(
soft = Color(0xFFF4E8FF), soft = CloserPalette.PurpleSoft,
accent = Color(0xFFE7A2D1), accent = CloserPalette.PinkBright,
deep = Color(0xFF6D2B55) deep = CloserPalette.PinkAccentDeep
) )
HomeActionTone.Starter -> HomeActionColors( HomeActionTone.Starter -> HomeActionColors(
soft = Color(0xFFFFE8F4), soft = CloserPalette.PinkSoft,
accent = Color(0xFFE7A2D1), accent = CloserPalette.PinkBright,
deep = Color(0xFF6D2B55) deep = CloserPalette.PinkAccentDeep
) )
HomeActionTone.Pack -> HomeActionColors( HomeActionTone.Pack -> HomeActionColors(
soft = Color(0xFFFFE8F4), soft = CloserPalette.PinkSoft,
accent = Color(0xFFE7A2D1), accent = CloserPalette.PinkBright,
deep = Color(0xFF6D2B55) deep = CloserPalette.PinkAccentDeep
) )
HomeActionTone.Utility -> HomeActionColors( HomeActionTone.Utility -> HomeActionColors(
soft = Color(0xFFF4E8FF), soft = CloserPalette.PurpleSoft,
accent = Color(0xFFB98AF4), accent = MaterialTheme.colorScheme.primary,
deep = Color(0xFF56306F) deep = CloserPalette.PurpleDeep
) )
} }
@ -546,15 +548,14 @@ private fun CategoryPreviewGrid(
Text( Text(
text = "More doorways", text = "More doorways",
style = MaterialTheme.typography.titleMedium, style = MaterialTheme.typography.titleMedium,
color = Color(0xFF2C2233), color = MaterialTheme.colorScheme.onSurface,
fontWeight = FontWeight.SemiBold fontWeight = FontWeight.SemiBold
) )
OutlinedButton( CloserActionButton(
label = "All packs",
onClick = onPacks, onClick = onPacks,
shape = RoundedCornerShape(14.dp) style = CloserButtonStyle.Secondary
) { )
Text("All packs")
}
} }
featuredCategories.chunked(2).forEach { rowItems -> featuredCategories.chunked(2).forEach { rowItems ->
@ -580,12 +581,12 @@ private fun CategoryMiniCard(
modifier: Modifier, modifier: Modifier,
onClick: () -> Unit onClick: () -> Unit
) { ) {
Card( CloserClickableCard(
onClick = onClick, onClick = onClick,
modifier = modifier, modifier = modifier,
shape = RoundedCornerShape(22.dp), containerColor = closerCardColor(alpha = 0.82f),
colors = CardDefaults.cardColors(containerColor = closerCardColor(alpha = 0.82f)), shape = RoundedCornerShape(CloserRadii.Card),
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp) elevation = CloserElevations.Card
) { ) {
Column( Column(
modifier = Modifier.padding(15.dp), modifier = Modifier.padding(15.dp),
@ -618,24 +619,7 @@ private fun CategoryMiniCard(
@Composable @Composable
private fun LoadingHomeCard() { private fun LoadingHomeCard() {
Card( LoadingState(message = "Opening your dashboard")
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(26.dp),
colors = CardDefaults.cardColors(containerColor = closerCardColor(alpha = 0.84f))
) {
Row(
modifier = Modifier.padding(22.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(14.dp)
) {
CircularProgressIndicator(color = Color(0xFFB98AF4))
Text(
text = "Opening your dashboard",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
} }
@Composable @Composable
@ -643,55 +627,21 @@ private fun ErrorHomeCard(
message: String, message: String,
onRefresh: () -> Unit onRefresh: () -> Unit
) { ) {
Card( ErrorState(
modifier = Modifier.fillMaxWidth(), title = "Home paused",
shape = RoundedCornerShape(26.dp), message = message,
colors = CardDefaults.cardColors(containerColor = closerCardColor(alpha = 0.84f)) retryLabel = "Retry",
) { onRetry = onRefresh
Column( )
modifier = Modifier.padding(20.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
Text(
text = "Home paused",
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.onSurface,
fontWeight = FontWeight.SemiBold
)
Text(
text = message,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Button(
onClick = onRefresh,
shape = RoundedCornerShape(16.dp),
colors = ButtonDefaults.buttonColors(
containerColor = Color(0xFFB98AF4),
contentColor = MaterialTheme.colorScheme.onPrimary
)
) {
Text("Retry")
}
}
}
} }
@Composable @Composable
private fun HomePill(label: String) { private fun HomePill(label: String) {
Surface( CloserPill(
shape = RoundedCornerShape(999.dp), label = label,
color = Color(0xFFFFF8FC) containerColor = CloserPalette.PinkMist.copy(alpha = 0.72f),
) { contentColor = MaterialTheme.colorScheme.onSurface
Text( )
text = label,
modifier = Modifier.padding(horizontal = 11.dp, vertical = 7.dp),
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.onSurface,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
} }
@Preview @Preview

View File

@ -93,7 +93,7 @@ fun CreateProfileScreen(
val context = LocalContext.current val context = LocalContext.current
LaunchedEffect(state.success) { LaunchedEffect(state.success) {
if (state.success) onNavigate(AppRoute.HOME) if (state.success) onNavigate(AppRoute.PAIR_PROMPT)
} }
LaunchedEffect(state.error) { LaunchedEffect(state.error) {
state.error?.let { snackbar.showSnackbar(it); viewModel.dismissError() } state.error?.let { snackbar.showSnackbar(it); viewModel.dismissError() }
@ -274,7 +274,7 @@ private fun SexStep(
) )
Spacer(Modifier.height(12.dp)) Spacer(Modifier.height(12.dp))
Text( Text(
text = "Some questions in Desire Sync and other features are tailored differently based on your sex. This helps us show you relevant questions.", text = "Some questions — especially in Desire Sync — are tailored based on your sex. This just helps us show you the right ones.",
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
color = AuthMuted, color = AuthMuted,
textAlign = TextAlign.Center textAlign = TextAlign.Center

View File

@ -0,0 +1,165 @@
package app.closer.ui.pairing
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import app.closer.core.navigation.AppRoute
import app.closer.ui.auth.AuthBackgroundBrush
import app.closer.ui.auth.AuthInk
import app.closer.ui.auth.AuthMuted
import app.closer.ui.auth.AuthOnPrimary
import app.closer.ui.auth.AuthPrimary
import app.closer.ui.auth.AuthPrimaryDeep
import app.closer.ui.theme.CloserPalette
private val WHY_ITEMS = listOf(
"Daily questions reveal together — both answer privately, then see each other's response at the same time.",
"Couples games: This or That, How Well Do You Know Me, Desire Sync, and more.",
"A shared timeline of answers, moments, and milestones you build together over time."
)
@Composable
fun PairPromptScreen(
onNavigate: (String) -> Unit = {}
) {
Column(
modifier = Modifier
.fillMaxSize()
.background(AuthBackgroundBrush)
.safeDrawingPadding()
.navigationBarsPadding()
.verticalScroll(rememberScrollState())
.padding(horizontal = 28.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(Modifier.height(56.dp))
Surface(
shape = RoundedCornerShape(20.dp),
color = AuthPrimary.copy(alpha = 0.12f)
) {
Text(
text = "You're in",
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp),
style = MaterialTheme.typography.labelLarge,
color = AuthPrimaryDeep,
fontWeight = FontWeight.SemiBold
)
}
Spacer(Modifier.height(20.dp))
Text(
text = "Now bring\nyour person in.",
style = MaterialTheme.typography.displaySmall.copy(fontWeight = FontWeight.Bold),
color = AuthInk,
textAlign = TextAlign.Center
)
Spacer(Modifier.height(16.dp))
Text(
text = "Closer is built for two. Connect your partner to unlock everything worth unlocking:",
style = MaterialTheme.typography.bodyLarge,
color = AuthMuted,
textAlign = TextAlign.Center
)
Spacer(Modifier.height(28.dp))
Column(
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(14.dp)
) {
WHY_ITEMS.forEach { item ->
WhyRow(text = item)
}
}
Spacer(Modifier.height(40.dp))
Button(
onClick = { onNavigate(AppRoute.CREATE_INVITE) },
modifier = Modifier
.fillMaxWidth()
.height(56.dp),
shape = RoundedCornerShape(16.dp),
colors = ButtonDefaults.buttonColors(
containerColor = AuthPrimary,
contentColor = AuthOnPrimary
)
) {
Text("Invite my partner", style = MaterialTheme.typography.labelLarge)
}
Spacer(Modifier.height(12.dp))
TextButton(
onClick = { onNavigate(AppRoute.HOME) },
modifier = Modifier.fillMaxWidth()
) {
Text(
"I'll do this later",
style = MaterialTheme.typography.bodyMedium,
color = AuthMuted
)
}
Spacer(Modifier.height(32.dp))
}
}
@Composable
private fun WhyRow(text: String) {
Row(
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.Top
) {
Surface(
shape = RoundedCornerShape(8.dp),
color = CloserPalette.PurpleDeep,
modifier = Modifier.size(22.dp)
) {
Icon(
imageVector = Icons.Filled.Check,
contentDescription = null,
tint = AuthOnPrimary,
modifier = Modifier
.padding(4.dp)
.size(14.dp)
)
}
Text(
text = text,
style = MaterialTheme.typography.bodyMedium,
color = AuthInk.copy(alpha = 0.82f)
)
}
}

View File

@ -22,10 +22,6 @@ import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Lock import androidx.compose.material.icons.filled.Lock
import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material.icons.filled.Star import androidx.compose.material.icons.filled.Star
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
@ -41,6 +37,12 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import app.closer.core.navigation.AppRoute import app.closer.core.navigation.AppRoute
import app.closer.ui.components.CategoryGlyph import app.closer.ui.components.CategoryGlyph
import app.closer.ui.components.CloserActionButton
import app.closer.ui.components.CloserButtonStyle
import app.closer.ui.components.CloserClickableCard
import app.closer.ui.components.CloserElevations
import app.closer.ui.components.CloserPill
import app.closer.ui.components.CloserRadii
import app.closer.ui.theme.CloserPalette import app.closer.ui.theme.CloserPalette
import app.closer.ui.theme.closerBackgroundBrush import app.closer.ui.theme.closerBackgroundBrush
import app.closer.ui.theme.closerBrandGlyphBrush import app.closer.ui.theme.closerBrandGlyphBrush
@ -183,14 +185,14 @@ private fun PlayHubContent(
private fun ThisOrThatCard( private fun ThisOrThatCard(
onClick: () -> Unit onClick: () -> Unit
) { ) {
Card( CloserClickableCard(
onClick = onClick, onClick = onClick,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.heightIn(min = 132.dp), .heightIn(min = 132.dp),
shape = RoundedCornerShape(24.dp), shape = RoundedCornerShape(CloserRadii.Card),
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface), containerColor = MaterialTheme.colorScheme.surface,
elevation = CardDefaults.cardElevation(defaultElevation = 6.dp) elevation = CloserElevations.Card
) { ) {
Row( Row(
modifier = Modifier modifier = Modifier
@ -200,7 +202,7 @@ private fun ThisOrThatCard(
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Surface( Surface(
shape = RoundedCornerShape(18.dp), shape = RoundedCornerShape(CloserRadii.Tile),
color = CloserPalette.PinkMist, color = CloserPalette.PinkMist,
modifier = Modifier.size(52.dp) modifier = Modifier.size(52.dp)
) { ) {
@ -228,18 +230,11 @@ private fun ThisOrThatCard(
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
) )
Surface( CloserPill(
shape = RoundedCornerShape(999.dp), label = "10 prompts",
color = CloserPalette.PinkMist containerColor = CloserPalette.PinkMist,
) { contentColor = CloserPalette.PinkAccentDeep
Text( )
text = "10 prompts",
modifier = Modifier.padding(horizontal = 10.dp, vertical = 4.dp),
style = MaterialTheme.typography.labelSmall,
color = CloserPalette.PinkAccentDeep,
fontWeight = FontWeight.SemiBold
)
}
} }
Text( Text(
text = "Rapid-fire A or B choices. See where you and your partner land.", text = "Rapid-fire A or B choices. See where you and your partner land.",
@ -263,12 +258,12 @@ private fun ThisOrThatCard(
private fun DesireSyncCard( private fun DesireSyncCard(
onClick: () -> Unit onClick: () -> Unit
) { ) {
Card( CloserClickableCard(
onClick = onClick, onClick = onClick,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(24.dp), shape = RoundedCornerShape(CloserRadii.Card),
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface), containerColor = MaterialTheme.colorScheme.surface,
elevation = CardDefaults.cardElevation(defaultElevation = 6.dp) elevation = CloserElevations.Card
) { ) {
Row( Row(
modifier = Modifier modifier = Modifier
@ -300,7 +295,7 @@ private fun DesireSyncCard(
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
) )
Surface( Surface(
shape = RoundedCornerShape(999.dp), shape = RoundedCornerShape(CloserRadii.Pill),
color = CloserPalette.Romantic.copy(alpha = 0.12f) color = CloserPalette.Romantic.copy(alpha = 0.12f)
) { ) {
Row( Row(
@ -345,12 +340,12 @@ private fun DesireSyncCard(
private fun HowWellCard( private fun HowWellCard(
onClick: () -> Unit onClick: () -> Unit
) { ) {
Card( CloserClickableCard(
onClick = onClick, onClick = onClick,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(24.dp), shape = RoundedCornerShape(CloserRadii.Card),
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface), containerColor = MaterialTheme.colorScheme.surface,
elevation = CardDefaults.cardElevation(defaultElevation = 6.dp) elevation = CloserElevations.Card
) { ) {
Row( Row(
modifier = Modifier modifier = Modifier
@ -382,18 +377,11 @@ private fun HowWellCard(
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
modifier = Modifier.weight(1f) modifier = Modifier.weight(1f)
) )
Surface( CloserPill(
shape = RoundedCornerShape(999.dp), label = "10 rounds",
color = CloserPalette.Romantic.copy(alpha = 0.12f) containerColor = CloserPalette.Romantic.copy(alpha = 0.12f),
) { contentColor = CloserPalette.Romantic
Text( )
text = "10 rounds",
modifier = Modifier.padding(horizontal = 10.dp, vertical = 4.dp),
style = MaterialTheme.typography.labelSmall,
color = CloserPalette.Romantic,
fontWeight = FontWeight.SemiBold
)
}
} }
Text( Text(
text = "One answers, the other predicts. Find out how well you really know each other.", text = "One answers, the other predicts. Find out how well you really know each other.",
@ -417,14 +405,14 @@ private fun HowWellCard(
private fun ConnectionChallengesCard( private fun ConnectionChallengesCard(
onClick: () -> Unit onClick: () -> Unit
) { ) {
Card( CloserClickableCard(
onClick = onClick, onClick = onClick,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.heightIn(min = 132.dp), .heightIn(min = 132.dp),
shape = RoundedCornerShape(24.dp), shape = RoundedCornerShape(CloserRadii.Card),
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface), containerColor = MaterialTheme.colorScheme.surface,
elevation = CardDefaults.cardElevation(defaultElevation = 6.dp) elevation = CloserElevations.Card
) { ) {
Row( Row(
modifier = Modifier modifier = Modifier
@ -434,7 +422,7 @@ private fun ConnectionChallengesCard(
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Surface( Surface(
shape = RoundedCornerShape(18.dp), shape = RoundedCornerShape(CloserRadii.Tile),
color = CloserPalette.PurpleDeep.copy(alpha = 0.12f), color = CloserPalette.PurpleDeep.copy(alpha = 0.12f),
modifier = Modifier.size(52.dp) modifier = Modifier.size(52.dp)
) { ) {
@ -461,18 +449,11 @@ private fun ConnectionChallengesCard(
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
) )
Surface( CloserPill(
shape = RoundedCornerShape(999.dp), label = "7 days",
color = CloserPalette.PurpleDeep.copy(alpha = 0.10f) containerColor = CloserPalette.PurpleDeep.copy(alpha = 0.10f),
) { contentColor = CloserPalette.PurpleDeep
Text( )
text = "7 days",
modifier = Modifier.padding(horizontal = 10.dp, vertical = 4.dp),
style = MaterialTheme.typography.labelSmall,
color = CloserPalette.PurpleDeep,
fontWeight = FontWeight.SemiBold
)
}
} }
Text( Text(
text = "Pick a series and build a small habit together, one day at a time.", text = "Pick a series and build a small habit together, one day at a time.",
@ -496,14 +477,14 @@ private fun ConnectionChallengesCard(
private fun MemoryLaneCard( private fun MemoryLaneCard(
onClick: () -> Unit onClick: () -> Unit
) { ) {
Card( CloserClickableCard(
onClick = onClick, onClick = onClick,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.heightIn(min = 132.dp), .heightIn(min = 132.dp),
shape = RoundedCornerShape(24.dp), shape = RoundedCornerShape(CloserRadii.Card),
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface), containerColor = MaterialTheme.colorScheme.surface,
elevation = CardDefaults.cardElevation(defaultElevation = 6.dp) elevation = CloserElevations.Card
) { ) {
Row( Row(
modifier = Modifier modifier = Modifier
@ -513,7 +494,7 @@ private fun MemoryLaneCard(
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Surface( Surface(
shape = RoundedCornerShape(18.dp), shape = RoundedCornerShape(CloserRadii.Tile),
color = CloserPalette.Romantic.copy(alpha = 0.12f), color = CloserPalette.Romantic.copy(alpha = 0.12f),
modifier = Modifier.size(52.dp) modifier = Modifier.size(52.dp)
) { ) {
@ -555,12 +536,12 @@ private fun MemoryLaneCard(
private fun FeaturedPlayCard( private fun FeaturedPlayCard(
onClick: () -> Unit onClick: () -> Unit
) { ) {
Card( CloserClickableCard(
onClick = onClick, onClick = onClick,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(30.dp), shape = RoundedCornerShape(CloserRadii.FeatureCard),
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface.copy(alpha = 0.9f)), containerColor = MaterialTheme.colorScheme.surface.copy(alpha = 0.9f),
elevation = CardDefaults.cardElevation(defaultElevation = 12.dp) elevation = CloserElevations.Feature
) { ) {
Column( Column(
modifier = Modifier modifier = Modifier
@ -573,30 +554,16 @@ private fun FeaturedPlayCard(
horizontalArrangement = Arrangement.SpaceBetween, horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Surface( CloserPill(
shape = RoundedCornerShape(999.dp), label = "Wheel",
color = MaterialTheme.colorScheme.surface.copy(alpha = 0.72f) containerColor = MaterialTheme.colorScheme.surface.copy(alpha = 0.72f),
) { contentColor = CloserPalette.PurpleDeep
Text( )
text = "Wheel", CloserPill(
modifier = Modifier.padding(horizontal = 12.dp, vertical = 7.dp), label = "10 prompts",
style = MaterialTheme.typography.labelMedium, containerColor = MaterialTheme.colorScheme.secondaryContainer,
color = CloserPalette.PurpleDeep, contentColor = CloserPalette.PinkAccentDeep
fontWeight = FontWeight.SemiBold )
)
}
Surface(
shape = RoundedCornerShape(999.dp),
color = MaterialTheme.colorScheme.secondaryContainer
) {
Text(
text = "10 prompts",
modifier = Modifier.padding(horizontal = 12.dp, vertical = 7.dp),
style = MaterialTheme.typography.labelMedium,
color = CloserPalette.PinkAccentDeep,
fontWeight = FontWeight.SemiBold
)
}
} }
Row( Row(
@ -628,19 +595,16 @@ private fun FeaturedPlayCard(
} }
} }
Button( CloserActionButton(
label = "Choose category",
onClick = onClick, onClick = onClick,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.heightIn(min = 54.dp), .heightIn(min = 54.dp),
shape = RoundedCornerShape(18.dp), style = CloserButtonStyle.Primary,
colors = ButtonDefaults.buttonColors( containerColor = CloserPalette.PurpleDeep,
containerColor = CloserPalette.PurpleDeep, contentColor = MaterialTheme.colorScheme.surface
contentColor = MaterialTheme.colorScheme.surface )
)
) {
Text("Choose category")
}
} }
} }
} }
@ -654,12 +618,12 @@ private fun CompactPlayCard(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onClick: () -> Unit onClick: () -> Unit
) { ) {
Card( CloserClickableCard(
onClick = onClick, onClick = onClick,
modifier = modifier.heightIn(min = 154.dp), modifier = modifier.heightIn(min = 154.dp),
shape = RoundedCornerShape(24.dp), shape = RoundedCornerShape(CloserRadii.Card),
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface), containerColor = MaterialTheme.colorScheme.surface,
elevation = CardDefaults.cardElevation(defaultElevation = 6.dp) elevation = CloserElevations.Card
) { ) {
Column( Column(
modifier = Modifier modifier = Modifier
@ -668,7 +632,7 @@ private fun CompactPlayCard(
verticalArrangement = Arrangement.SpaceBetween verticalArrangement = Arrangement.SpaceBetween
) { ) {
Surface( Surface(
shape = RoundedCornerShape(18.dp), shape = RoundedCornerShape(CloserRadii.Tile),
color = tint.copy(alpha = 0.14f), color = tint.copy(alpha = 0.14f),
modifier = Modifier.size(46.dp) modifier = Modifier.size(46.dp)
) { ) {