> ## Documentation Index
> Fetch the complete documentation index at: https://substrate.docs.unknowncreatives.studio/llms.txt
> Use this file to discover all available pages before exploring further.

# Android: Compose Token Files and the substrate.kernel Port

> Substrate emits per-mode Compose files declaring SubstrateSystemTokens in Display P3, a Material 3 ColorScheme bridge, and a Kotlin port of the color kernel.

The Compose output mirrors the Swift output closely: **per-mode token files** with statically resolved colors, a **Material 3 ColorScheme bridge** for interop with existing theming, and a **Kotlin port of the color kernel** for anything continuous.

***

## What gets generated

Each brand emits a directory under `generated/brands/<brand>/compose/`:

| File                             | Contents                                                     |
| -------------------------------- | ------------------------------------------------------------ |
| `system.light.gen.kt`            | Resolved tokens at `scheme: 0`, `contrastFactor: 1.0`        |
| `system.dark.gen.kt`             | Resolved tokens at `scheme: 1`, `contrastFactor: 1.0`        |
| `system.highContrast.gen.kt`     | `scheme: 0`, `contrastFactor: 1.3`                           |
| `system.darkHighContrast.gen.kt` | `scheme: 1`, `contrastFactor: 1.3`                           |
| `material-scheme.gen.kt`         | Material 3 `ColorScheme` values for light and dark           |
| `brand.gen.kt`                   | Brand data the kernel evaluates                              |
| `index.gen.kt`                   | Aggregate entry point: brand data plus component descriptors |

As on iOS, there is no `system.dimmed.gen.kt` — a mid-track position at `scheme: 0.65` has no endpoint to bake and needs continuous sampling through the kernel.

***

## The token files

Example output for a fictional `acme` brand — `generated/brands/acme/compose/system.light.gen.kt`:

```kotlin theme={null}
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.colorspace.ColorSpaces

data class SubstrateSystemTokenSet(
    val surface: Color,
    val text: Color,
    val border: Color,
)

object SubstrateSystemTokens {
    val surface = SubstrateSystemTokenSet(
        surface = Color(red = 0.9588f, green = 0.9594f, blue = 0.9728f, alpha = 1f, colorSpace = ColorSpaces.DisplayP3),
        text = Color(red = 0.0424f, green = 0.0427f, blue = 0.0511f, alpha = 1f, colorSpace = ColorSpaces.DisplayP3),
        border = Color(red = 0.4431f, green = 0.4436f, blue = 0.4553f, alpha = 1f, colorSpace = ColorSpaces.DisplayP3),
    )
    val brand = SubstrateSystemTokenSet(
        surface = Color(red = 0.3475f, green = 0.1504f, blue = 0.9177f, alpha = 1f, colorSpace = ColorSpaces.DisplayP3),
        text = Color(red = 0.3673f, green = 0.1900f, blue = 0.9455f, alpha = 1f, colorSpace = ColorSpaces.DisplayP3),
        border = Color(red = 0.5581f, green = 0.5264f, blue = 1.0000f, alpha = 1f, colorSpace = ColorSpaces.DisplayP3),
    )
}
```

Colors are **Display P3**, and each intent is a surface/text/border triple — the same three fields as Swift. The roles are `surface`, `surfaceElevated`, `brand`, `neutral`, `danger`, `warning`, and `success`, plus any custom intents the brand declares. There are no spacing or type-size constants in the token output.

<Warning>
  **The generated Compose files declare no package.** They go straight from the generated header to the imports, so there is no package line to edit and no import statement to write in consuming code — the declarations are top-level in whatever source set you add them to.

  All four per-mode files declare the *same* top-level names, so adding two of them to one source set fails to compile on duplicate `SubstrateSystemTokens` and `SubstrateSystemTokenSet` declarations. Include **exactly one per build variant** — for example, via variant-specific source sets in Gradle.
</Warning>

```kotlin theme={null}
@Composable
fun BalanceCard() {
    Column(
        modifier = Modifier
            .background(SubstrateSystemTokens.surfaceElevated.surface)
            .border(1.dp, SubstrateSystemTokens.surface.border)
            .padding(16.dp)
    ) {
        Text("Balance due", color = SubstrateSystemTokens.surface.text)
        Text("$42.00", color = SubstrateSystemTokens.brand.text)
    }
}
```

***

## The Material ColorScheme bridge

If your app already themes through Material 3, `material-scheme.gen.kt` is the integration point. It maps Substrate's solved intents onto Material's role names and exposes a ready `ColorScheme` for each of light and dark:

```kotlin theme={null}
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme

object AcmeMaterialScheme {
    val light: ColorScheme = lightColorScheme(
        primary = Color(red = 0.3475f, green = 0.1504f, blue = 0.9177f, alpha = 1f, colorSpace = ColorSpaces.DisplayP3),
        onPrimary = Color(red = 0.9999f, green = 1.0000f, blue = 1.0000f, alpha = 1f, colorSpace = ColorSpaces.DisplayP3),
        error = Color(red = 0.6942f, green = 0.0659f, blue = 0.1203f, alpha = 1f, colorSpace = ColorSpaces.DisplayP3),
        background = Color(red = 0.9588f, green = 0.9594f, blue = 0.9728f, alpha = 1f, colorSpace = ColorSpaces.DisplayP3),
        onBackground = Color(red = 0.0424f, green = 0.0427f, blue = 0.0511f, alpha = 1f, colorSpace = ColorSpaces.DisplayP3),
        outline = Color(red = 0.4431f, green = 0.4436f, blue = 0.4553f, alpha = 1f, colorSpace = ColorSpaces.DisplayP3),
    )

    val dark: ColorScheme = darkColorScheme(/* … */)
}
```

The object is named per brand, so an `acme` brand produces `AcmeMaterialScheme`. Unlike the per-mode token files, this one carries both schemes in a single file, so runtime light/dark selection is straightforward:

```kotlin theme={null}
@Composable
fun AppTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) {
    MaterialTheme(
        colorScheme = if (darkTheme) AcmeMaterialScheme.dark
                      else AcmeMaterialScheme.light,
        content = content,
    )
}
```

<Note>
  Material's role vocabulary is narrower than Substrate's. The bridge covers the Material roles; intents outside that set — custom brand intents, `beta`, `info` — stay available through `SubstrateSystemTokens` or the kernel.
</Note>

***

## The kernel port

`packages/kernel-kotlin` mirrors the Swift package, in the `substrate.kernel` package: `Apca.kt`, `Oklch.kt`, `Cvd.kt`, `Warmth.kt`, `Track.kt`, `Pipeline.kt`, `Preferences.kt`, `Surface.kt`, `ColorResolve.kt`, `StyleDescriptor.kt`, and `BrandData.kt`. Unlike the generated token files, the kernel *does* declare a package, so you import from it normally:

```kotlin theme={null}
import substrate.kernel.CvdConfig
import substrate.kernel.CvdType
import substrate.kernel.UserPreferences
```

Reach for the kernel when static files can't express what you need: a continuous scheme position such as dimmed, a user-chosen `contrastFactor`, CVD compensation, or warmth. A conformance harness holds the port to the TypeScript kernel's computed values, so Android, iOS, and web agree on the same inputs.

***

## Component descriptors

Component styling ships as data, interpreted by the kernel. The per-brand `index.gen.kt` aggregates the brand data and each component's resolved descriptor — the brand override where one exists, otherwise the global descriptor:

```kotlin theme={null}
import substrate.kernel.BrandData
import substrate.kernel.ComponentStyleDescriptor

object MagicPatternsSubstrate {
    val brand: BrandData = MagicPatternsBrand.data

    val styles: Map<String, ComponentStyleDescriptor> = mapOf(
        "badge" to MagicPatternsBadgeComponent.style,
        "button" to MagicPatternsButtonComponent.style,
        "card" to CardComponent.style,
        "input" to MagicPatternsInputComponent.style,
    )
}
```

<Note>
  Every generated file carries a `@generated-substrate` header and is a build artifact. Never edit one — change the brand YAML and regenerate with `npm run generate:tokens:compose` from a Substrate checkout.
</Note>

***

<CardGroup cols={2}>
  <Card title="iOS / Swift" icon="apple" href="/platforms/ios-swift">
    The SwiftPM mirror of this output and the SubstrateKernel package.
  </Card>

  <Card title="Modes Overview" icon="sliders" href="/modes/overview">
    The preference vector behind the four baked modes, and why dimmed isn't one of them.
  </Card>
</CardGroup>
