Native Android CDS: Kotlin + Jetpack Compose, published as the AAR com.coinbase.cds:cds. Gradle
module :cds, Nx project cds-android.
This is a library, not application code. Everything public here is a promise to consumers that is expensive to take back, so the default answer to "should this be public?" is no.
:cds compiles with Kotlin explicit API mode,
so the compiler rejects any declaration whose visibility was inherited rather than chosen.
- Default to
internalorprivate. Reach forpubliconly when the symbol is meant for customers. - Hyrum's Law applies: consumers will depend on any
publicsymbol even if undocumented. Style resolvers (*Colors,*Metrics), assembly composables, and helpers stayinternal. - When the compiler tells you to add a visibility modifier, that is the moment to decide whether
the symbol belongs on the customer API - not a formality to satisfy with
public. - Never widen visibility to make
apps/android-appcompile. The demo app is a consumer. If it cannot express something with the public API, either the API is genuinely missing something or the app is doing something it should not. - Changing the signature of an existing
publicdeclaration is a breaking change. - The public surface lives in
com.coinbase.cds.themeandcom.coinbase.cds.components.button,com.coinbase.cds.interaction. Components such asTextandSlideButtonare temporarilyinternalfor the first release — they were experiments and are not customer API yet. Anything undercomponents/internal/stays off-limits to consumers by construction.
- Reading tokens:
CdsTheme.colors.bgPrimary,CdsTheme.space.x2, and the other companion accessors onCdsTheme. - Authoring a theme: the
cdsTheme { }builder. Token types have internal constructors on purpose - do not add public ones, and do not reintroduce acopy()-based or config-object API. LocalCdsThemeis public and read-only so customModifiernodes (CompositionLocalConsumerModifierNode) can read theme outside of composition. That is its only reason to be public: ordinary composables should useCdsTheme.*. Do not make it writable, and do not narrow it back tointernal.
Rationale for the theme design is in src/main/java/com/coinbase/cds/theme/README.md, and the
consumer-facing guides are in docs/.
Follow the jetpack-best-practices skill (the official AOSP Compose API guidelines). The rules
that get violated most often here: every element accepts and respects a Modifier parameter,
Modifier is the first optional parameter, and composables that emit UI return Unit.
When porting a component from packages/mobile or auditing an existing Android port for mobile
parity, load the cds-rn-to-compose skill. It covers discovery, RN→Compose mapping, interaction
hoisting, token usage, testing, and the audit checklist.
- Do not add Yarn/npm dependencies to this package. Its
package.jsonis a stub that exists only so Yarn workspaces and the Nx CI helpers can see the project; the real dependency list isbuild.gradle.ktsplusandroid/gradle/libs.versions.toml. - Do not import
@coinbase/cds-common. Kotlin cannot consume it. Tokens here are a hand-port of the shared set; keeping them in sync is manual until token codegen exists. compileSdkis deliberately 36 here while the demo app is on 37. AGP records this in the AAR metadata and hard-fails consumers compiling against anything lower, so raising it forces every consuming app to move. Raise it only when this module actually needs a newer API.- Gradle owns the version (
0.0.1inbuild.gradle.kts). It is unrelated to the 9.x npm versions and must never be pulled intoyarn release. Cutting a release is manual; followdocs/releasing.mdand record the version inCHANGELOG.md.
Run from the repo root:
yarn nx run cds-android:build # AAR -> packages/cds-android/build/outputs/aar/
yarn nx run cds-android:test # JUnit; headless composition, no RobolectricThe root CI workflow selects the reusable Gradle workflow (.github/workflows/android.yml) when
this package, apps/android-app, or android/ changes. New Android Nx projects use
toolchain:gradle; keep Gradle-specific jobs in the reusable workflow.
build/, .gradle/, .idea/, and local.properties are generated and stay untracked.