styles

Use this skill to integrate the Jetpack Compose Styles API into an Android project. This skill guides you through upgrading dependencies, setting up component…

INSTALLATION
npx skills add https://github.com/android/skills --skill styles
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

Limitations

  • Warn the user that this skill is EXPERIMENTAL and requires updating to alpha version of Compose and opting in to the Experimental APIs.
  • This skill only supports custom UI components and custom themes.
  • This skill does not support Material Design component Styles.

Prerequisites

1. Upgrade dependencies

  • The project must use compileSdk version 37 or higher.
  • The project must use androidx.compose.foundation:foundation version 1.12.0-alpha01 or higher.
  • Alternatively, the project must use Compose BOM version 2026.04.01 or higher.
  • The API requires this exact package: import androidx.compose.foundation.style.Style

2. Configure compiler options to enable experimental API

You must opt-in to the experimental API at the project level. Add the following

block to your module's build.gradle.kts:

kotlin {

    compilerOptions {

        jvmTarget = JvmTarget.fromTarget("17")

        freeCompilerArgs.add("-opt-in=androidx.compose.foundation.style.ExperimentalFoundationStyleApi")

    }

}

Core workflows and guides

Refer to the official documentation to complete specific development tasks:

Step-by-Step Migration Workflow

Step 1: Analyze theme structure

  • Locate your central theme file (such as Theme.kt).
  • Identify design tokens. Note references for colors, typography, and shapes (for example, LocalColorScheme, LocalTypography, or LocalShapes).
  • If the project lacks Jetpack Compose dependencies, stop. Instruct the user to migrate to Jetpack Compose first.
  • If the project imports androidx.compose.material.MaterialTheme, recommend migrating to Material 3 before proceeding.

Step 2: Establish ComponentStyles

-

Create a new file named ComponentStyles.kt in your theme directory.

-

Define a top-level data class to hold your component styles, for example, the Jetsnack one is called JetsnackStyles:

object ExampleComponentStyles {

    val customButtonStyle: Style = {

    }

    val customTextFieldStyle: Style = {

    }

}

-

Expose this class through your custom theme with a static reference, don't

use CompositionLocals here as it's not required.

@Immutable

class JetsnackTheme(

    // other Design system properties

) {

    companion object {

        val colors: CustomThemingWithStyles.JetsnackColors

            @Composable @ReadOnlyComposable

            get() = LocalJetsnackTheme.current.colors

        // ...

        // add helper static reference

        val styles: ComponentStyles = ComponentStyles

    }

}

-

Provide extensions on StyleScope to reference theme tokens directly if

they are exposed using CompositionLocals. For example:

val StyleScope.colors: JetsnackColors

    get() = LocalJetsnackTheme.currentValue.colors

val StyleScope.typography: androidx.compose.material3.Typography

    get() = LocalJetsnackTheme.currentValue.typography

val StyleScope.shapes: Shapes

    get() = LocalJetsnackTheme.currentValue.shapes

Step 3: Migrate a component to Styles API

For each custom component (for example, CustomButton), complete the following

sequence:

  • Establish a visual baseline (If an emulator is available):
  • If you CANNOT run an Android emulator: Skip this step entirely and proceed to Step 2.
  • If you CAN run an Android emulator: Perform the following to capture a baseline screenshot:
  • Option A: Locate and run an existing screenshot test for the component.
  • Option B (If no test exists): Create a test using the project's existing testing framework, then run it.
  • Option C (If no framework exists): Create a minimal screenshot test using UI Automator or Espresso, then run it.
  • Remove individual styling parameters : Remove styling parameters such as backgroundColor, shape, textStyle, and contentPadding from the signature - anything that StyleScope supports.
  • Add the style parameter : Add style: Style = Style to the function signature. Always ensure the default value is exactly Style (e.g., style: Style = Style) and not a specific style default like ChipStyleDefault or any other value.
  • Declare state tracking : If the component is interactable, create a MutableStyleState using the interaction source. Update state fields (such as isEnabled) inside the Composable to track the state correctly.
  • Apply styleable modifier : Replace specific layout modifiers on the root element with Modifier.styleable().
  • Move defaults to ComponentStyles : Move hardcoded values from the component definition to a dedicated Style instance in ComponentStyles.kt.
  • Validate component: Compare the baseline screenshot image taken at the start with the rendered Compose Preview of the new composable. Ignore string content; focus on layout and styling. Iterate on the Compose code until visual parity is achieved. Once verified, write a Compose UI test for the new composable.

#### Migration example

Before Migration:

@Composable

fun CustomButton(

    onClick: () -> Unit,

    modifier: Modifier = Modifier,

    backgroundColor: Color = JetsnackTheme.colors.brandLight,

    disabledBackgroundColor: Color = JetsnackTheme.colors.brandSecondary,

    shape: Shape = JetsnackTheme.shapes.extraLarge,

    textStyle: TextStyle = JetsnackTheme.typography.labelLarge,

    enabled: Boolean = true,

    content: @Composable RowScope.() -> Unit,

) {

    val interactionSource = remember { MutableInteractionSource() }

    Row(

        modifier

            .clickable(onClick = onClick, indication = null, interactionSource = interactionSource)

            .background(if (enabled) backgroundColor else disabledBackgroundColor, shape)

            .defaultMinSize(58.dp, 40.dp),

        horizontalArrangement = Arrangement.Center,

        verticalAlignment = Alignment.CenterVertically,

        content = content,

    )

}

After Migration:

// Exposed via ComponentStyles.kt

object ComponentStyles {

    val buttonStyle = Style {

        background(colors.brandLight)

        shape(shapes.extraLarge)

        minWidth(58.dp)

        minHeight(40.dp)

        textStyle(typography.labelLarge)

        disabled {

            background(colors.brandSecondary)

        }

    }

}

@Composable

fun CustomButton(

    onClick: () -> Unit,

    modifier: Modifier = Modifier,

    style: Style = Style,

    enabled: Boolean = true,

    content: @Composable RowScope.() -> Unit,

) {

    val interactionSource = remember { MutableInteractionSource() }

    val styleState = rememberUpdatedStyleState(interactionSource) {

        it.isEnabled = enabled

    }

    Row(

        modifier

            .clickable(onClick = onClick, indication = null, interactionSource = interactionSource)

            .styleable(styleState, JetsnackTheme.styles.buttonStyle, style),

        horizontalArrangement = Arrangement.Center,

        verticalAlignment = Alignment.CenterVertically,

        content = content,

    )

}

Step 4: Validate Changes

  • Build the project. Verify that there are no compilation errors.
  • Run your module's screenshot tests.
  • Compare visual outputs of the whole app between the previous and updated components. Verify that no visual layout regressions occur.
BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card