Skapa for Android version 3.0 Migration Guide

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.

Overview

Skapa for Android 3.0 introduces significant improvements to the design system, including:

Release Plan for 3.X.X

  1. Alpha(s) will be released first for early adopters and feedback. (Not recommended to be used in production code)
  2. Based on feedback, a stable version will be released
  3. Post-release, monitor for issues and provide patches as needed
  4. Clean up release where all code deprecated with ERROR level is removed

Plans for 2.X.X track

After 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.

Migration Process

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.07.00 (Compose 1.8.X). Ensure your project is compatible with this version.

  1. Make sure you are using the latest stable version of Android Studio. (Android Studio Narwhal 3 Feature Drop | 2025.1.3)
  2. Latest build tools and Android SDK version 36 is also recommended
  3. Go through all current deprecation warnings in your project and address them before starting the migration
  4. Backup your project before starting the migration
  5. Clean your project: Run ./gradlew clean to remove cached issues
  6. Update dependencies to use Skapa BOM version 2025.09.00 or later
  7. Follow the component-specific migration steps below
  8. Test thoroughly after each component migration

Component Migration Guide

1. SkapaTheme

Migration Type: BREAKING CHANGE (ERROR level deprecation)

What Changed:

What to Do:

// 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:

  1. Replace all SkapaTheme with SkapaTheme2
  2. Replace SkapaThemeM3 with SkapaTheme2 if you do not need the old typography system
  3. Configure theme parameters as needed
  4. Ensure you are using correct SkapaTypeScale configuration depending device size. The default is set to Auto

2. Typography System

Migration Type: BREAKING CHANGE

What Changed:

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:

  1. Ensure correct theme wrapper is used at top-level (SkapaTheme2)
  2. Replace all SkapaTheme.typography with SkapaTheme.typographyM2
  3. Replace all SkapaTheme.typography2 with SkapaTheme.typography
  4. Review typography usage and ensure correct styles are applied, some typesets may not map directly and give different visual results

3. Pill Component

Migration 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:

  1. Review all Pill component usage
  2. Update to use the new single constructor, use the replaceWith feature if needed
  3. You may need to add null 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 release
  4. Test thoroughly to ensure visual consistency

4. Price Module

Migration Type: BREAKING CHANGE

What Changed:

Before (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:

  1. Replace all instances of mergeAllDescendants with mergeDescendantsStrategy
  2. Test to ensure keyboard navigation and Talkback works as expected

5. AspectRatioBox

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:

  1. Replace all old aspect ratio objects with SkapaAspectRatio equivalents. For example SkapaAspectRatio.Standard becomes SkapaAspectRatio.Ratio4by3
  2. Update all AspectRatioBox usages to the new unified constructor
  3. Test to ensure aspect ratios and content alignment behave as expected

6. Carousel

Migration Type: BREAKING CHANGE

What Changed:

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:

  1. Replace all instances of CarouselVariant.OverflowWithoutIndicator with CarouselVariant.Overflow(showIndicator = false)
  2. Test to ensure carousel functionality and indicators behave as expected

7. ModalHeaderParams / BottomSheet ( Material 2 ) to ModalHeader / Sheet ( Material 3 )

Migration Type: BREAKING CHANGE

What Changed:

New 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:

  1. Replace all usage of BottomSheet (M2) with Sheet
  2. Replace all usage of BottomSheetM3 with Sheet
  3. Replace all usage of ModalHeaderParams with ModalHeader
  4. Update state management to handle the new Sheets open and close logic

8. TextArea

Migration Type: BREAKING CHANGE

What Changed:

Before (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:

  1. Replace all instances of enabled: Boolean with enabledState: EnabledState
  2. Use EnabledState.Enabled, EnabledState.Disabled, or EnabledState.ReadOnly as appropriate
  3. Test to ensure text area functionality behaves as expected

9. DualButton

Migration Type: BREAKING CHANGE

What Changed:

@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")
            }
        }
    )
}

10. Divider

Migration Type: BREAKING CHANGE

What Changed:

@Composable
fun DividerExample() {
    Divider() // Default horizontal orientation, no parameters available

    Divider(orientation = Orientation.Horizontal)

    Divider(orientation = Orientation.Vertical)
}

11. InputField

Migration Type: BREAKING CHANGE

What Changed:

@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:

  1. Update parameters to use new constructors
  2. Test to ensure input field functionality behaves as expected

12. Card Components (CardEmphasised / CardRegular)

Migration Type: BREAKING CHANGE

What Changed:

Using new Card (V2):

@Composable
fun CardExample() {
    Card(
        title = "Title",
        modifier = Modifier.fillMaxWidth(),
        subTitle = "Subtitle",
        body = "Body text",
        variant = CardV2Variant.Regular,
        titleSize = CardV2TitleSize.HeadingL,
        cardTheme = CardTheme.Default
    ) {
        /* Click action */
    }
}

Action Required:

  1. Replace all instances of CardEmphasised and CardRegular with Card
  2. Update parameters to match the new Card component, including variant, titleSize, addon, mediaContainer and cardTheme
  3. Test to ensure card functionality and appearance behave as expected

13. Button / IconButton

Migration Type: BREAKING CHANGE

What Changed:

Before (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 */
    }
}

14. Checkbox / TristateCheckbox

Migration Type: BREAKING CHANGE

What Changed:

Before (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"
    )
}

Post-Migration Steps

1. Clean Build

After completing the migration, clean your project to remove cached issues:

./gradlew clean

2. Build and Test

Compile your project to ensure all changes are applied correctly:

./gradlew build

3. Run Tests

Execute your test suite to verify functionality:

./gradlew test

4. Visual Testing

Perform thorough visual testing to ensure UI components render correctly with the new design system.


Common Issues and Solutions

Issue: Compilation Errors After Migration

Solution:

  1. Perform a clean build: ./gradlew clean
  2. Invalidate caches in Android Studio
  3. Ensure all deprecated APIs have been replaced
  4. Check for any lingering errors in the IDE

Issue: Typography Not Displaying Correctly

Solution:

  1. Verify you're using SkapaTheme2 wrapper
  2. Check typography references are updated correctly
  3. Ensure proper theme application at app level

Issue: Component Layout Issues

Solution:

  1. Review parameter changes for affected components
  2. Test with different screen sizes and orientations
  3. Verify accessibility features still work correctly

Additional Resources


Support

If you encounter issues during migration:

  1. Check this guide for component-specific solutions
  2. Review the changelog for detailed technical changes
  3. Contact the Skapa team for additional support

Contacts

Last Updated: September 04, 2025 Guide Version: 1.0 Target Skapa Version: 3.0 Target Skapa-BOM Version: 2025.09.00-alpha01