Blocks

CircleUp Onboarding

An AR social app onboarding slide with floating feature pills, progress dots and dual sign-up CTAs.

CircleUp

Intuitive
Full integration
😊

Easy to use

Just select the AR button when interacting with posts, choose your desired effect or object, and watch as it magically appears on your screen

Installation

caveui components are copy-paste Jetpack Compose built entirely on Material 3 — there's no caveui dependency to add. Make sure Material 3 is on your classpath (it ships with the Compose BOM), then copy the Usage snippet below into your project.

kotlin
// build.gradle.kts (module)
dependencies {
    implementation(platform("androidx.compose:compose-bom:2025.06.00"))
    implementation("androidx.compose.material3:material3")
}

Usage

kotlin
@Composable
fun CircleUpOnboardingScreen(
    page: Int = 2,
    totalPages: Int = 4,
    onSignUp: () -> Unit,
    onLogIn: () -> Unit,
) {
    val indigo = Color(0xFF2D3282)
    val bg = Color(0xFF8B8FD4)

    Column(
        modifier = Modifier
            .fillMaxSize()
            .background(bg)
            .padding(horizontal = 20.dp),
        horizontalAlignment = Alignment.CenterHorizontally,
    ) {
        Spacer(Modifier.height(28.dp))
        Text(
            "CircleUp",
            style = MaterialTheme.typography.titleLarge,
            fontWeight = FontWeight.Bold,
            color = Color.White,
        )
        Spacer(Modifier.height(16.dp))

        // Illustration card
        Box(
            modifier = Modifier
                .fillMaxWidth()
                .height(220.dp)
                .clip(RoundedCornerShape(20.dp))
                .background(Color(0xFF6B6FC4)),
        ) {
            // Stars
            repeat(4) { i ->
                Text(
                    "✦",
                    color = Color.White.copy(alpha = 0.5f),
                    fontSize = if (i % 2 == 0) 10.sp else 7.sp,
                    modifier = Modifier.padding(
                        start = (16 + i * 40).dp,
                        top = (12 + i * 14).dp,
                    ),
                )
            }
            // Person silhouette
            Box(
                modifier = Modifier
                    .align(Alignment.BottomCenter)
                    .width(96.dp)
                    .height(140.dp)
                    .clip(
                        RoundedCornerShape(topStart = 48.dp, topEnd = 48.dp)
                    )
                    .background(Color.White.copy(alpha = 0.18f)),
            )
            // VR headset bar
            Box(
                modifier = Modifier
                    .align(Alignment.Center)
                    .width(72.dp)
                    .height(20.dp)
                    .offset(y = 8.dp)
                    .clip(RoundedCornerShape(6.dp))
                    .background(Color(0xFF1a1a2e).copy(alpha = 0.7f)),
            )
            // "Intuitive" pill
            Surface(
                color = Color.White,
                shape = RoundedCornerShape(12.dp),
                modifier = Modifier
                    .align(Alignment.TopStart)
                    .padding(start = 12.dp, top = 36.dp),
            ) {
                Text(
                    "Intuitive",
                    modifier = Modifier.padding(horizontal = 10.dp, vertical = 6.dp),
                    style = MaterialTheme.typography.labelLarge,
                    fontWeight = FontWeight.Bold,
                    color = Color(0xFF1a1a2e),
                )
            }
            // "Full integration" pill
            Surface(
                color = Color.White,
                shape = RoundedCornerShape(12.dp),
                modifier = Modifier
                    .align(Alignment.BottomEnd)
                    .padding(end = 10.dp, bottom = 36.dp),
            ) {
                Text(
                    "Full integration",
                    modifier = Modifier.padding(horizontal = 10.dp, vertical = 6.dp),
                    style = MaterialTheme.typography.labelLarge,
                    fontWeight = FontWeight.Bold,
                    color = Color(0xFF1a1a2e),
                )
            }
            // Emoji badge
            Surface(
                shape = CircleShape,
                color = Color.White,
                modifier = Modifier
                    .align(Alignment.TopEnd)
                    .padding(end = 20.dp, top = 56.dp)
                    .size(40.dp),
            ) {
                Box(contentAlignment = Alignment.Center) {
                    Text("😊", fontSize = 20.sp)
                }
            }
            // AR icon badge
            Surface(
                shape = CircleShape,
                color = Color.White,
                border = BorderStroke(2.dp, Color(0xFFE8612C)),
                modifier = Modifier
                    .align(Alignment.BottomStart)
                    .padding(start = 14.dp, bottom = 48.dp)
                    .size(40.dp),
            ) {
                Box(contentAlignment = Alignment.Center) {
                    Icon(
                        Icons.Outlined.ViewInAr,
                        contentDescription = "AR",
                        tint = Color(0xFFE8612C),
                        modifier = Modifier.size(20.dp),
                    )
                }
            }
        }

        Spacer(Modifier.height(20.dp))
        // Progress dots
        Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
            repeat(totalPages) { i ->
                Box(
                    Modifier
                        .height(8.dp)
                        .width(if (i == page) 24.dp else 8.dp)
                        .clip(CircleShape)
                        .background(
                            if (i == page) Color.White
                            else Color.White.copy(alpha = 0.4f),
                        ),
                )
            }
        }

        Spacer(Modifier.height(16.dp))
        Text(
            "Easy to use",
            style = MaterialTheme.typography.headlineMedium,
            fontWeight = FontWeight.ExtraBold,
            color = Color.White,
        )
        Text(
            "Just select the AR button when interacting with posts, choose your desired effect or object, and watch as it magically appears on your screen",
            modifier = Modifier.padding(top = 8.dp),
            style = MaterialTheme.typography.bodyMedium,
            color = Color.White.copy(alpha = 0.8f),
            textAlign = TextAlign.Center,
        )

        Spacer(Modifier.height(24.dp))
        Button(
            onClick = onSignUp,
            modifier = Modifier.fillMaxWidth(),
            shape = RoundedCornerShape(14.dp),
            colors = ButtonDefaults.buttonColors(containerColor = indigo),
        ) {
            Text(
                "Sign Up",
                style = MaterialTheme.typography.bodyLarge,
                fontWeight = FontWeight.Bold,
            )
        }
        TextButton(onClick = onLogIn, modifier = Modifier.padding(top = 4.dp)) {
            Text(
                "Log in",
                color = Color.White,
                style = MaterialTheme.typography.bodyMedium,
            )
        }
    }
}