Skip to main content

Theme Kit (Views-first)

Alpha Theme Kit lives in cabin-views / CabinViews. It resolves cabin-tokens semantic and day/night scheme roles onto theme attributes so widgets never hardcode hex and OEMs brand without forking chrome (ADR 0003).

System Bar / Status Bar Views chrome also lives in cabin-views and consumes Theme Kit for day/night container / outline / safety roles (MVP #4). Compose mirrors the same roles via resolveCabinColors / CabinTheme in cabin-compose (Experimental; no Views theme attrs).

Artifacts

PathIdentifier
Gradledev.decoupled.cabin:cabin-views (Alpha source module)
SoongCabinViews
Packagedev.decoupled.cabin.views.theme

Theme Kit itself needs cabin-tokens / CabinTokens only. Full Views chrome (bars) also depends on cabin-compliance / CabinCompliance. No AppCompat, Material, or Compose.

What resolves

Theme attrToken resourceDay/night
cabin_colorPrimary / OnPrimarycabin_color_semantic_*Semantic (stub baseline)
cabin_colorSecondary / OnSecondarysemanticSemantic
cabin_colorSurface / OnSurface / SurfaceVariantcabin_color_scheme_*Yes (values-night)
cabin_colorOutlineschemeYes
cabin_colorContainer / OnContainerschemeYes
cabin_colorWarning / Error / ChargingschemeYes (night contrast-locked)
cabin_colorSuccess / Climate / MediaAccent / ScrimsemanticSemantic

Styles:

  • Theme.Cabin / Theme.Cabin.DayNight — platform DayNight parent + token attrs
  • ThemeOverlay.Cabin — layer Cabin attrs on an existing host theme (SystemUI)
  • ThemeOverlay.Cabin.OemBrandDemo — illustrative brand remap (primary only)

Kotlin: CabinThemes, CabinThemeResolver.resolveColors(context).

Apply (Gradle apps)

<application android:theme="@style/Theme.Cabin">
val colors = CabinThemeResolver.resolveColors(context)
// colors.outline / container flip with UiMode night

SystemUI and other build-tree targets use Soong CabinViews + theme overlay — not Gradle implementation of Cabin AARs (build-tree).

OEM overlay / RRO (demoable without forking widgets)

Brand at the token / theme boundary. Widgets keep resolving cabin_* attrs; products never copy Theme Kit or future bar sources.

Apps (Gradle theme overlay)

  1. Create an OEM module that depends only on cabin-tokens (optional thin overlay AAR) or ship overlay styles in the product APK.
  2. Remap brand-facing roles:
<style name="ThemeOverlay.Oem.Cabin" parent="">
<item name="cabin_colorPrimary">@color/oem_brand_primary</item>
<item name="cabin_colorOnPrimary">@color/oem_brand_on_primary</item>
<!-- Do NOT remap cabin_colorWarning / cabin_colorError for decoration -->
</style>
  1. Apply over Cabin:
android:theme="@style/Theme.Cabin"
<!-- activity / ContextThemeWrapper also applies ThemeOverlay.Oem.Cabin -->

Cabin ships ThemeOverlay.Cabin.OemBrandDemo as a demoable pattern — Robolectric tests prove primary remaps while warning/error/charging stay locked.

Platform (RRO + optional Soong token overlay)

Prefer Runtime Resource Overlays on device images:

// Illustrative — product tree, not Cabin core
runtime_resource_overlay {
name: "OemCabinThemeRRO",
resource_dirs: ["res"],
manifest: "AndroidManifest.xml",
sdk_version: "system_current",
// target package: SystemUI or the package hosting Theme.Cabin
}

RRO res/values/colors.xml overrides CabinTokens color resources (or product theme overlays remapping the same cabin_* attrs). Do not overlay layout XML for bars just to change brand colors.

Optional: OemCabinTokens Soong module depending on CabinTokens that supplies brand color resources; Theme Kit attrs continue to point at stable resource names.

Sketch XML for an RRO color remap: sketches/oem-cabin-rro/.

Locked roles

RoleRule
warning / errorSafety-adjacent — no decorative remaps
Night warning / error / chargingContrast-locked (no soft-wash)
onSurface / onContainerNeutral body text — never domain accents

See token-schema.

Thin deps

SystemUI-shaped targets pull tokens + compliance + views:

static_libs: ["CabinTokens", "CabinCompliance", "CabinViews"]
// Do NOT add CabinCompose, catalog, or samples

Theme-only experiments can omit CabinCompliance; restriction-aware bars need it.

Verification

./gradlew :cabin-views:test

Day/night scheme resolution + OEM overlay smoke are covered by Robolectric unit tests (testing).