Spotlight your UI, not a screenshot of it.
A Compose Multiplatform coachmark library that creates true transparent cutouts in the overlay scrim. Your actual UI remains visible and interactive through the spotlight - animations play, buttons respond, nothing is faked.
Supports Android, iOS, Desktop, and Web via Kotlin Multiplatform.
Live Demo | Dashboard Demo — Try it in your browser, no install required.
Add to your shared module's build.gradle.kts:
kotlin {
sourceSets {
commonMain.dependencies {
implementation("io.github.aldefy:lumen:1.0.0-beta06")
}
}
}This resolves the correct artifact per target automatically:
| Target | Artifact |
|---|---|
| Android | lumen-android (AAR) |
| iOS arm64 | lumen-iosarm64 (klib) |
| iOS Simulator arm64 | lumen-iossimulatorarm64 (klib) |
| iOS Simulator x64 | lumen-iosx64 (klib) |
| Desktop (JVM) | lumen-jvm (JAR) |
| Web (Wasm) | lumen-wasmjs (klib) |
If your project is not using KMP:
dependencies {
implementation("io.github.aldefy:lumen-android:1.0.0-beta06")
}val controller = rememberCoachmarkController()
CoachmarkHost(controller = controller) {
// Your screen content
Button(
onClick = { },
modifier = Modifier.coachmarkTarget(controller, "my-button")
) {
Text("Click me")
}
}
// Show the coachmark
controller.show(
CoachmarkTarget(
id = "my-button",
title = "Welcome",
description = "Tap here to get started.",
)
)Single coachmark with pulse animation Multi-step onboarding tour
For the full API documentation, visit the docs site.
| Component | Description |
|---|---|
CoachmarkHost |
Wraps your content and renders the overlay scrim |
CoachmarkController |
Manages coachmark state, show/dismiss operations |
CoachmarkTarget |
Defines a single spotlight target with tooltip content |
Modifier.coachmarkTarget() |
Tags a composable as a coachmark target |
| Property | Type | Description |
|---|---|---|
id |
String |
Unique identifier for the target |
title |
String |
Tooltip headline |
description |
String |
Tooltip body text |
shape |
CutoutShape |
Shape of the transparent cutout |
tooltipPosition |
TooltipPosition |
Where tooltip appears relative to target |
connectorStyle |
ConnectorStyle |
Style of line connecting cutout to tooltip |
connectorEndStyle |
ConnectorEndStyle |
Endpoint decoration (DOT, ARROW, NONE, CUSTOM) |
highlightAnimation |
HighlightAnimation |
Animation effect on the cutout |
targetTapBehavior |
TargetTapBehavior |
What happens when user taps the cutout area |
showDontShowAgain |
Boolean |
Show "Don't show again" checkbox |
ctaText |
String |
Call-to-action button text |
| Shape | Description |
|---|---|
Circle |
Circular cutout, ideal for FABs and icons |
Rect |
Rectangular cutout with sharp corners |
RoundedRect |
Rectangle with rounded corners |
Squircle |
iOS-style superellipse with smooth curves |
Star |
Star shape for gamification highlights |
| Animation | Description |
|---|---|
NONE |
Static cutout, no animation |
PULSE |
Gentle breathing/scaling effect |
GLOW |
Pulsing stroke width and opacity |
RIPPLE |
Expanding rings emanating outward |
SHIMMER |
Highlight sweeping around the stroke |
BOUNCE |
Energetic scale with overshoot |
| Style | Description |
|---|---|
AUTO |
Auto-select based on tooltip position |
VERTICAL |
Straight vertical line |
HORIZONTAL |
Straight horizontal line |
ELBOW |
L-shaped with 90° bend |
DIRECT |
Diagonal line pointing to tooltip |
CURVED |
Smooth quadratic Bezier curve |
| Style | Description |
|---|---|
DOT |
Small filled circle (default) |
ARROW |
Directional arrowhead toward tooltip |
NONE |
No endpoint decoration |
CUSTOM |
Custom rendering via DrawScope lambda |
| Property | Default | Description |
|---|---|---|
scrimOpacity |
MEDIUM |
Darkness of the overlay (LIGHT, MEDIUM, DARK, EXTRA_DARK) |
scrimTapBehavior |
DISMISS |
Action when tapping outside cutout |
backPressBehavior |
DISMISS |
Action on back press |
showSkipButton |
false |
Show skip button in tooltip |
showProgressIndicator |
true |
Show step dots for sequences |
delayBeforeShow |
0L |
Milliseconds to wait before showing |
connectorCutoutGap |
12.dp |
Minimum gap between cutout animation edge and connector dot |
ctaMinWidth |
Dp.Unspecified |
Minimum width for the CTA button (unspecified = wrap content) |
ctaMinHeight |
48.dp |
Minimum height for the CTA button |
ctaCornerRadius |
22.dp |
Corner radius for the CTA button shape |
controller.show(target) // Show single coachmark
controller.showSequence(targets) // Show multi-step tour
controller.next() // Advance to next step
controller.previous() // Go back one step
controller.dismiss() // Hide coachmark
controller.enabled = false // Disable all coachmarksA Compose Multiplatform app with interactive demos for all features.
- Android: Open the project in Android Studio and run the
sampleconfiguration. - iOS: Open
iosApp/iosApp.xcodeprojin Xcode and run on a simulator or device. - Desktop: Run
./gradlew :sample:runfrom the terminal to launch the desktop window. - Web: Run
./gradlew :sample:wasmJsBrowserRunto open the sample in a browser.
A web-only (wasmJs) dashboard demo showing Lumen coachmarks in a real-world SaaS onboarding scenario. Features a 7-step auto-starting feature tour with varied cutout shapes, animations, and connector styles.
- Web: Run
./gradlew :sample-web:wasmJsBrowserDevelopmentRunto open the dashboard in a browser. - Live Dashboard Demo
| Platform | Min Version | Artifact |
|---|---|---|
| Android | API 23 | lumen (via Gradle metadata) or lumen-android |
| iOS arm64 | iOS 16 | lumen-iosarm64 |
| iOS Simulator arm64 | iOS 16 | lumen-iossimulatorarm64 |
| iOS Simulator x64 | iOS 16 | lumen-iosx64 |
| Desktop (JVM) | JDK 17 | lumen-jvm |
| Web (Wasm) | Modern browser | lumen-wasmjs |
- Kotlin 2.0+
- Compose Multiplatform 1.7+
- Android API 23+ / iOS 16+ / JDK 17+ / Modern browsers (Chrome, Firefox, Safari)
Copyright 2024 Adit Lal
Licensed under the Apache License, Version 2.0


