This guide provides comprehensive instructions for migrating your Android project from Skapa for latest 2.X release (skapa-bom: 2025.07.01) to Skapa for Android 3.0. All breaking changes are documented with before/after code examples and detailed explanations.
Skapa for Android 3.0 introduces significant improvements to the design system, including:
replaceWith suggestions will not be available after this stepAfter the first stable release of 3.X.X version of Skapa libraries all feature development together with improvements and non-critical bug fixes will stop for 2.X.X track. Critical fixes and showstopper issues may still be provided. This means you are expected to migrate in order to get new features.
The migration guide is based on migration from the latest 2.X version to 3.0 for all packages. All the Skapa 3.0 libraries depend on Compose BOM 2025.10.0 (Compose 1.9.X, Material3 1.4.X). Ensure your project is compatible with this version.
./gradlew clean to remove cached issuesreplaceWith suggestions so make use of these. In most cases you can also "replace all occurences in the project"replaceWith updates will add some dead code based on your use of parameters. Use the Context actions again to "Simplify expressions".Migration Type: BREAKING CHANGE (ERROR level deprecation)
What Changed:
SkapaTheme is completely deprecatedSkapaThemeM3 receives WARNING level deprecationskapa-bom:2025.02.02. Changelog hereWhat to Do:
SkapaTheme with SkapaTheme2SkapaThemeM3 with SkapaTheme2 unless you need access to the previous typography system// Usage of old Material 2 SkapaTheme. Deprecated with ERROR level
@Composable
fun ExampleScreen() {
SkapaTheme(darkTheme = true) {
// Your content
}
}
// Usage of old Material 3 SkapaTheme. Deprecated with WARNING level
@Composable
fun ExampleScreen() {
SkapaThemeM3(darkTheme = true) {
// Your content
}
}
// Usage of new SkapaTheme. No deprecation.
@Composable
fun ExampleScreen() {
SkapaTheme2(darkTheme = true) {
// Your content
}
}
Action Required:
SkapaTheme with SkapaTheme2SkapaThemeM3 with SkapaTheme2 if you do not need the old typography systemdarkTheme: BooleanfontFamily: FontFamilytypeScale: SkapaTypeScalebaseFontSize: TextUnitSkapaTypeScale configuration depending device size. The default is set to AutoMigration Type: BREAKING CHANGE
What Changed:
typography becomes the primary typography system, previously typography2typography2 has been deprecated with WARNING leveltypography from typographyM2typographyM2 has been deprecated with WARNING leveltypographyM3 remains unchanged (but still deprecated with WARNNG)Before (Skapa 2.0):
@Composable
fun TypographyExample() {
// Old typography, using Skapa Typesets
Text(
text = "Heading L",
style = SkapaTheme.typesets.heading.headingL
)
// Old typography, using Material 2 mapping system
Text(
text = "Heading 1",
style = SkapaTheme.typography.h1
)
// Old typography, using Material 3 mapping system
Text(
text = "Headline Large",
style = SkapaTheme.typographyM3.headlineLarge
)
// Latest typography system, using new Skapa typography system
Text(
text = "Heading L",
style = SkapaTheme.typography2.headingL
)
}
After (Skapa 3.0):
@Composable
fun TypographyExample() {
// Old typography, using Skapa Typesets
// Remains unchanged for now but will be deprecated in future release with WARNING level
Text(
text = "Heading L",
style = SkapaTheme.typesets.heading.headingL
)
// Old typography, using Material 2 mapping system
// Note: Now typographyM2 instead of typography
Text(
text = "Heading 1",
style = SkapaTheme.typographyM2.h1
)
// Old typography, using Material 3 mapping system
// Remains unchanged
Text(
text = "Headline Large",
style = SkapaTheme.typographyM3.headlineLarge
)
// Latest typography system, using new Skapa typography system
// Note: Now typography instead of typography2
Text(
text = "Heading L",
style = SkapaTheme.typography.headingL
)
}
Action Required:
SkapaTheme2)SkapaTheme.typography with SkapaTheme.typographyM2SkapaTheme.typography2 with SkapaTheme.typographyMigration Type: BREAKING CHANGE
What Changed:
campaignSustainability color value updated to a static value. Updated to #37b886Before (Skapa 2.0):
@Composable
fun SustainabilityBackgroundExample() {
Box(modifier = Modifier
.background(SkapaTheme.colors.campaignSustainability)
.fillMaxSize()
) {
Text(
modifier = Modifier.padding(16.dp),
text = "Sustainability Example",
color = SkapaTheme.colors.textAndIcon5,
style = SkapaTheme.typography.bodyM
)
}
}
After (Skapa 3.0):
@Composable
fun CheckboxExample() {
Box(modifier = Modifier
.background(SkapaTheme.colors.campaignSustainability)
.fillMaxSize()
) {
Text(
modifier = Modifier.padding(16.dp),
text = "Sustainability Example",
color = SkapaTheme.colors.staticBlack,
style = SkapaTheme.typography.bodyM
)
}
}
Migration Type: BREAKING CHANGE
What Changed:
horizontalPadding into contentHorizontalPadding and headerHorizontalPadding. The old functions are now deprecated with level=DeprecationLevel.ERROR.Before (Skapa 2.0):
@Composable
fun AccordionExample() {
Accordion(
title = "Title",
caption = "Caption",
open = isOpen,
horizontalPadding = 0.dp, // Deprecated
onClick = {}
) { /* Accordion content */ }
}
After (Skapa 3.0):
@Composable
fun AccordionExample() {
Accordion(
title = "Title",
caption = "Caption",
open = isOpen,
headerHorizontalPadding = 0.dp, // Use contentHorizontalPadding and headerHorizontalPadding instead.
contentHorizontalPadding = 0.dp,
onClick = {}
) { /* Accordion content */ }
}
Action Required:
horizontalPadding parameter with headerHorizontalPadding and contentHorizontalPadding.ReplaceWith suggestions are available for this.Migration Type: BREAKING CHANGE (Constructor consolidated)
What Changed:
Before (Skapa 2.0):
@Composable
fun AspectRatioExample() {
// Separate constructors
AspectRatioBox(
aspectRatio = AspectRatio.Square,
backgroundColor = Color.Blue
)
AspectRatioBox(
aspectRatio = AspectRatio.Widescreen,
contentAlignment = Alignment.Center
)
}
After (Skapa 3.0):
@Composable
fun AspectRatioExample() {
// Unified constructor
AspectRatioBox(
aspectRatio = SkapaAspectRatio.Ratio1by1,
backgroundColor = SkapaTheme.colors.neutral2,
contentAlignment = Alignment.Center
)
}
Action Required:
SkapaAspectRatio equivalents. For example SkapaAspectRatio.Standard becomes SkapaAspectRatio.Ratio4by3AspectRatioBox usages to the new unified constructorMigration Type: BREAKING CHANGE
What Changed:
IconButtonBefore (Skapa 2.0):
@Composable
fun ButtonExample() {
Button(
variant = ButtonVariant.PrimaryInverse,
text = "Click me"
) {
/* Click action */
}
Button(
variant = ButtonVariant.SecondaryInverse,
text = "Click me"
) {
/* Click action */
}
Button(
variant = ButtonVariant.TertiaryInverse,
text = "Click me"
) {
/* Click action */
}
}
After (Skapa 3.0):
@Composable
fun ButtonExample() {
Button(
variant = ButtonVariant.Primary.Inverse,
text = "Click me"
) {
/* Click action */
}
Button(
variant = ButtonVariant.Secondary.Inverse,
text = "Click me"
) {
/* Click action */
}
Button(
variant = ButtonVariant.Tertiary.Inverse,
text = "Click me"
) {
/* Click action */
}
}
Migration Type: BREAKING CHANGE
What Changed:
Card component insteadCardV2Variant has been deprecated with level.ERROR and replaced with CardVariant.CardV2TitleSize has been deprecated with level.ERROR and replaced with CardTitleSize.Using new Card (V2):
@Composable
fun CardExample() {
Card(
title = "Title",
modifier = Modifier.fillMaxWidth(),
subTitle = "Subtitle",
body = "Body text",
variant = CardVariant.Regular,
titleSize = CardTitleSize.HeadingL,
cardTheme = CardTheme.Default
) {
/* Click action */
}
}
Action Required:
CardEmphasised and CardRegular with CardCard component, including variant, titleSize, addon, mediaContainer and cardThemeCardV2Variant to CardVariant and CardV2TitleSize to CardTitleSizeMigration Type: BREAKING CHANGE
What Changed:
CarouselVariant.OverflowWithoutIndicator deprecated with ERROR levelCarouselVariant.Overflow(showIndicator: Boolean)Before (Skapa 2.0):
@Composable
fun CarouselExample() {
val carouselItems = remember { listOf("Item 1", "Item 2", "Item 3") }
Carousel(
variant = CarouselVariant.OverflowWithoutIndicator,
items = carouselItems
)
}
After (Skapa 3.0):
@Composable
fun CarouselExample() {
val carouselItems = remember { listOf("Item 1", "Item 2", "Item 3") }
Carousel(
variant = CarouselVariant.Overflow(showIndicator = false),
items = carouselItems
)
}
Action Required:
CarouselVariant.OverflowWithoutIndicator with CarouselVariant.Overflow(showIndicator = false)Migration Type: BREAKING CHANGE
What Changed:
error: Boolean and helperText: String? deprecatedcaption and helperTextBefore (Skapa 2.0):
@Composable
fun CheckboxExample() {
var isChecked by remember { mutableStateOf(false) }
var checkboxState by remember { mutableStateOf(ToggleableState.Off) }
Checkbox(
label = "Checkbox label",
checked = isChecked,
onCheckedChange = { isChecked = it },
error = true,
helperText = "This is helper text with error state"
)
TristateCheckbox(
label = "Checkbox label",
state = checkboxState,
onClick = { /* handle click */ },
error = false,
helperText = "This is optional helper text without error state"
)
}
After (Skapa 3.0):
@Composable
fun CheckboxExample() {
var isChecked by remember { mutableStateOf(false) }
var checkboxState by remember { mutableStateOf(ToggleableState.Off) }
Checkbox(
label = "Checkbox label",
checked = isChecked,
onCheckedChange = { isChecked = it },
caption = "Checkbox caption",
helperTextErrorLabel = "This is an error helper text"
)
TristateCheckbox(
label = "Tristate checkbox label",
state = checkboxState,
onClick = { /* handle click */ },
caption = "Tristate checkbox caption",
helperTextErrorLabel = "This is an error helper text"
)
}
Migration Type: BREAKING CHANGE
What Changed:
modifier and without orientation deprecated with ERROR level@Composable
fun DividerExample() {
Divider() // Default horizontal orientation, no parameters available
Divider(orientation = Orientation.Horizontal)
Divider(orientation = Orientation.Vertical)
}
Migration Type: BREAKING CHANGE
What Changed:
DualButtonOrientation deprecated with ERROR levelOrientation class@Composable
fun DualButtonExample() {
DualButton(
orientation = Orientation.Horizontal,
primaryButton = {
Button(onClick = {}) {
Text("Primary")
}
},
secondaryButton = {
Button(onClick = {}) {
Text("Secondary")
}
}
)
DualButton(
orientation = Orientation.Vertical,
primaryButton = {
Button(onClick = {}) {
Text("Primary")
}
},
secondaryButton = {
Button(onClick = {}) {
Text("Secondary")
}
}
)
}
Migration Type: BREAKING CHANGE
What Changed:
enabledState, helperTextParams)@Composable
fun InputFieldExample() {
var inputValue by remember { mutableStateOf("") }
InputField(
value = inputValue,
onValueChange = { inputValue = it },
label = "Enter text",
// New enhanced parameters available
enabledState = EnabledState.Enabled,
helperTextParams = HelperTextParams(BaseField.State.Default.toHelperState(), label = "Help Text"),
)
}
Action Required:
Migration Type: BREAKING CHANGE
What Changed:
ModalHeaderParams completely deprecated with ERROR level, this was used in Material 2 based BottomSheetBottomSheet (M2) completely deprecated with ERROR levelModalHeader component directly in the new Material 3 based SheetBottomSheetM3 deprecated with WARNING level, use Sheet insteadNew Sheet is based on the Material 3 ModalBottomSheet component and has been refactored to align more closely with it
Material 3 ModalBottomSheet examples and tutorials can be used as reference for the new Sheet component
@Composable
fun SheetExample() {
val open = mutableStateOf(true)
val topPadding = WindowInsets.statusBars.asPaddingValues().calculateTopPadding()
if (open.value) {
Sheet(
onDismiss = { open.value = false },
modifier = Modifier.padding(top = topPadding),
sheetState = rememberModalBottomSheetState(),
modalHeader = ModalHeader.Regular( /* Set modal header parameters */ ), // This is the major change
sheetFooterParams = ModalsActionFooterParams( /* Set footer parameters */ ),
content = { /* Sheet content */ }
)
}
}
Action Required:
BottomSheet (M2) with SheetBottomSheetM3 with SheetModalHeaderParams with ModalHeaderSheets open and close logicMigration Type: BREAKING CHANGE (Constructor function removed)
NOTE: It is important to know that due to similar parameters that do not have defaults in the old constructors, to fully access the new constructor you may need to add null for optional parameters. This will be temporarily required until the old constructors are removed in a future release
What Changed:
@Composable
fun PillExample() {
var pillState by remember { mutableStateOf(true) }
Pill(
text = "Label",
selected = pillState,
leadingItem = null,
trailingIconId = R.drawable.icon_sample,
badgeValue = 5
) {
pillState = !pillState
}
}
fun PillExample() {
var pillState by remember { mutableStateOf(true) }
Pill(
text = "Label",
selected = pillState,
leadingItem = null,
trailingIconId = null,
badgeValue = null
) {
pillState = !pillState
}
}
Action Required:
replaceWith feature if needednull for optional parameters due to conflicting function signatures with the old deprecated constructors, this will be temporarily required until the old constructors are removed in a future releaseMigration Type: BREAKING CHANGE
What Changed:
PriceSizeStyle has been moved from constructor into the relevant PriceVariants. Applicable for PriceVariant.Regular, PriceVariant.BTI, PriceVariant.TimeRestrictedOfferPriceSizeStyle.SingleSize has been renamed to PriceSizeStyle.FixedSize for consistency.PriceVariant.BTI has been changed from a data object to a class, hence the need to change from PriceVariant.BTI to PriceVariant.BTI(...)PriceVariant.Regular has been updated to use PriceVariantStyle.Emphasised and PriceVariantStyle.Subtle instead of true/false for subtle, regularFontWeight parameters.PriceVariant.Comparison has been updated to use PriceVariantStyle.Emphasised and PriceVariantStyle.Subtle instead of true/false for subtle, regularFontWeight parameters.PriceVariant.Comparison now always uses strikeout font featureBefore (Skapa 2.0):
@Composable
fun PriceExample() {
// PriceSizeStyle and Regular variant
Price(
integerValue = integerValue,
decimalValue = decimalValue,
currency = currency,
variant = PriceVariant.Regular(subtle = false, regularFontWeight = false),
subscriptLabel = label,
priceSizeStyle = PriceSizeStyle.MixedSize
)
// Comparison
Price(
integerValue = integerValue,
decimalValue = decimalValue,
currency = currency,
variant = PriceVariant.Comparison(strikeout = true, subtle = false, regularFontWeight = false),
subscriptLabel = label
)
}
After (Skapa 3.0):
@Composable
fun PriceExample() {
// PriceSizeStyle and Regular variant
Price(
integerValue = integerValue,
decimalValue = decimalValue,
currency = currency,
variant = PriceVariant.Regular(style = PriceVariantStyle.Emphasised, priceSizeStyle = PriceSizeStyle.MixedSize),
subscriptLabel = label
)
// Comparison
Price(
integerValue = integerValue,
decimalValue = decimalValue,
currency = currency,
variant = PriceVariant.Comparison(style = PriceVariantStyle.Emphasised),
subscriptLabel = label
)
}
Action Required:
campaignSustainability background with SkapaTheme.colors.staticBlack or another color that meets accessibility requirementsMigration Type: BREAKING CHANGE
What Changed:
mergeAllDescendants parameter replaced with mergeDescendantsStrategyBoolean to the MergeDescendantsStrategy enum classBefore (Skapa 2.0):
@Composable
fun PriceModuleExample() {
PriceModule(
currentPriceParams = PriceParams( /* price parameters */ ),
mergeAllDescendants = true
)
}
After (Skapa 3.0):
@Composable
fun PriceModuleExample() {
PriceModule(
currentPriceParams = PriceParams( /* price parameters */ ),
mergeDescendantsStrategy = MergeDescendantsStrategy.All // Read the KDoc for use-cases
)
}
Action Required:
mergeAllDescendants with mergeDescendantsStrategyMigration Type: BREAKING CHANGE
What Changed:
SegmentedControlSize.Text enum class instead.SegmentedControlSize.Icon enum class instead.Before (Skapa 2.0):
@Composable
fun SegmentedControlExample() {
var selectedLabel by remember { mutableStateOf("x") }
// List of items of text type
val itemsLabel = listOf(
SegmentedControlItem.Text("x", "Label x"),
SegmentedControlItem.Text("y", "Label y"),
SegmentedControlItem.Text("z", "Label z")
)
SegmentedControl(
textItems = itemsLabel,
size = SegmentedControlTextItemSize.Medium, // Old sizing for text and icon items
selectedKey = selectedLabel,
onSelect = { selectedLabel = it }
)
}
After (Skapa 3.0):
@Composable
fun SegmentedControlExample() {
var selectedLabel by remember { mutableStateOf("x") }
// List of items of text type
val itemsLabel = listOf(
SegmentedControlItem.Text("x", "Label x"),
SegmentedControlItem.Text("y", "Label y"),
SegmentedControlItem.Text("z", "Label z")
)
SegmentedControl(
textItems = itemsLabel,
size = SegmentedControlSize.Text.Small, // Updated sizing for text and icon items
selectedKey = selectedLabel,
onSelect = { selectedLabel = it }
)
}
Action Required:
SegmentedControlTextItemSize with SegmentedControlSize.TextSegmentedControlIconItemSize with SegmentedControlSize.IconMigration Type: BREAKING CHANGE
What Changed:
enabled: Boolean deprecated with ERROR levelenabledState: EnabledState insteadBefore (Skapa 2.0):
@Composable
fun TextAreaExample() {
var textValue by remember { mutableStateOf("") }
TextArea(
value = textValue,
onValueChange = { textValue = it },
enabled = true
)
}
After (Skapa 3.0):
@Composable
fun TextAreaExample() {
var textValue by remember { mutableStateOf("") }
TextArea(
value = textValue,
onValueChange = { textValue = it },
enabledState = EnabledState.Enabled
)
// For disabled state
TextArea(
value = textValue,
onValueChange = { textValue = it },
enabledState = EnabledState.Disabled
)
// For read-only state
TextArea(
value = textValue,
onValueChange = { textValue = it },
enabledState = EnabledState.ReadOnly
)
}
Action Required:
enabled: Boolean with enabledState: EnabledStateEnabledState.Enabled, EnabledState.Disabled, or EnabledState.ReadOnly as appropriateAfter completing the migration, clean your project to remove cached issues:
./gradlew clean
Compile your project to ensure all changes are applied correctly:
./gradlew build
Execute your test suite to verify functionality:
./gradlew test
Perform thorough visual testing to ensure UI components render correctly with the new design system.
Solution:
./gradlew cleanSolution:
SkapaTheme2 wrapperSolution:
If you encounter issues during migration:
Contacts
Last Updated: September 04, 2025 Guide Version: 1.0 Target Skapa Version: 3.0 Target Skapa-BOM Version: 2025.09.00-alpha01