Material Design 3 — KMP / Compose Multiplatform Skill
This skill guides implementation of Google's Material Design 3 (MD3 / Material You) using
Jetpack Compose and Compose Multiplatform for KMP projects (Android + Desktop).
Attribution: This skill is adapted from and gives credit to
hamen/material-3-skill by Hamen.
The original skill covers web and Flutter targets. This KMP edition strips all web/CSS/Flutter
patterns and replaces them with pure Compose Multiplatform APIs, adds the audit report system,
and extends the reference set for adaptive layout, navigation, and versioning.
Scope: Compose-only. All examples use androidx.compose.material3 APIs — the same API
surface is available in KMP commonMain via org.jetbrains.compose.material3:material3
(declared explicitly in libs.versions.toml). No web CSS, no @material/web elements, no Flutter.
MD3 Philosophy
| Principle |
What it means in Compose |
| Personal |
Dynamic color from user wallpaper (Android 12+). Static fallback for Desktop. |
| Adaptive |
WindowSizeClass drives layout changes across compact → expanded screens. |
| Expressive |
Spring-based motion, shape morphing, emphasized typography. |
Google I/O 2026 Key Updates
- Compose-first on Android: For all new Android work, use
androidx.compose.material3.
- Expressive layout scaffold: Design screens to adapt across mobile, desktop, foldables. Use
Material3Adaptive scaffold APIs.
- 8dp spacing system: Define spacing as tokens — never scatter raw
Dp literals.
- New expressive components: Lists, menus, search, and search app bars have refreshed expressive guidance; check your Material3 BOM for expressive variants.
Design Token System
All MD3 values come through MaterialTheme. Never hardcode raw values inline.
| Token category |
Access in Compose |
| Color |
MaterialTheme.colorScheme.* |
| Typography |
MaterialTheme.typography.* |
| Shape |
MaterialTheme.shapes.* |
| Spacing |
Define a Dimens object (no built-in API) |
Decision Tree
What are you building?
Full app scaffold → AppTheme setup + references/theming-and-dynamic-color.md
Single component → references/component-catalog.md
Custom color theme → references/color-system.md
Typography / fonts → references/typography-and-shape.md
Navigation structure → references/navigation-patterns.md
Adaptive layout → references/layout-and-responsive.md
Color Token Summary
Full details in references/color-system.md.
Key Roles (Compose token → usage)
| Role |
Token |
Primary Usage |
| Primary |
colorScheme.primary |
FAB, key buttons, active states |
| On Primary |
colorScheme.onPrimary |
Text/icons on primary |
| Primary Container |
colorScheme.primaryContainer |
Tonal buttons, selected chips |
| On Primary Container |
colorScheme.onPrimaryContainer |
Text on primary container |
| Secondary |
colorScheme.secondary |
Less prominent accents, filters |
| Secondary Container |
colorScheme.secondaryContainer |
Recessive fills |
| Tertiary |
colorScheme.tertiary |
Contrasting accent sections |
| Surface |
colorScheme.surface |
Cards, sheets, menus |
| Surface Container |
colorScheme.surfaceContainer |
Navigation areas |
| On Surface |
colorScheme.onSurface |
Body text, icons |
| On Surface Variant |
colorScheme.onSurfaceVariant |
Placeholder, helper text |
| Outline |
colorScheme.outline |
Input borders, dividers |
| Error |
colorScheme.error |
Error states |
Typography Token Summary
Full details in references/typography-and-shape.md.
| Category |
Styles |
Usage |
| Display |
L / M / S |
Hero text, large numbers |
| Headline |
L / M / S |
Screen/section headers |
| Title |
L / M / S |
Toolbar titles, card headers |
| Body |
L / M / S |
Paragraph text, descriptions |
| Label |
L / M / S |
Buttons, chips, captions |
// ✅ Always via MaterialTheme
Text("Title", style = MaterialTheme.typography.titleLarge)
Text("Body", style = MaterialTheme.typography.bodyMedium)
// ❌ Never inline
Text("Title", fontSize = 22.sp, fontWeight = FontWeight.Normal)
Shape Token Summary
| Token |
Corner Radius |
Typical Components |
shapes.extraSmall |
4dp |
Chips, snackbars |
shapes.small |
8dp |
Text fields, menus |
shapes.medium |
12dp |
Cards |
shapes.large |
16dp |
FABs, nav drawer |
shapes.extraLarge |
28dp |
Dialogs, bottom sheets |
Elevation
MD3 communicates depth through tonal surface color, not drop shadows.
| Level |
Compose API |
Tonal Offset |
Use |
| 0 |
Elevation.Level0 / 0.dp |
None |
Flat surfaces at rest |
| 1 |
Elevation.Level1 / 1.dp |
+5% primary |
Elevated cards |
| 2 |
Elevation.Level2 / 3.dp |
+8% primary |
Menus, nav bar |
| 3 |
Elevation.Level3 / 6.dp |
+11% primary |
FAB, dialogs |
// Cards respect tonal elevation automatically via surfaceTonalElevation
ElevatedCard(elevation = CardDefaults.elevatedCardElevation(defaultElevation = 6.dp)) { }
Motion Summary
Full details in references/typography-and-shape.md §Motion.
| Easing |
Compose |
Usage |
| Emphasized |
CubicBezierEasing(0.2f, 0f, 0f, 1f) |
Elements staying on screen |
| Emphasized Decelerate |
CubicBezierEasing(0.05f, 0.7f, 0.1f, 1f) |
Entering screen |
| Emphasized Accelerate |
CubicBezierEasing(0.3f, 0f, 0.8f, 0.15f) |
Leaving screen |
| Standard |
FastOutSlowInEasing |
Utility animations |
Standard Durations
| Token |
Duration |
Usage |
| Short |
100–200ms |
Icon/color state changes |
| Medium |
300–400ms |
Component expand/collapse |
| Long |
400–500ms |
Screen-level transitions |
Component Quick Reference
| Component |
Compose API |
Category |
| Button (Filled) |
Button {} |
Actions |
| Button (Tonal) |
FilledTonalButton {} |
Actions |
| Button (Outlined) |
OutlinedButton {} |
Actions |
| Button (Text) |
TextButton {} |
Actions |
| FAB |
FloatingActionButton {} |
Actions |
| Extended FAB |
ExtendedFloatingActionButton {} |
Actions |
| Icon Button |
IconButton {}, FilledIconButton {} |
Actions |
| Segmented Button |
SegmentedButton {} |
Actions |
| Card |
Card {}, ElevatedCard {}, OutlinedCard {} |
Containment |
| Dialog |
AlertDialog {}, Dialog {} |
Containment |
| Bottom Sheet |
ModalBottomSheet {} |
Sheets |
| Snackbar |
SnackbarHost {} |
Communication |
| Progress |
CircularProgressIndicator(), LinearProgressIndicator() |
Communication |
| Badge |
BadgedBox {} |
Communication |
| Checkbox |
Checkbox() |
Input |
| RadioButton |
RadioButton() |
Input |
| Switch |
Switch() |
Input |
| Slider |
Slider(), RangeSlider() |
Input |
| TextField |
TextField(), OutlinedTextField() |
Input |
| Chips |
FilterChip, AssistChip, InputChip, SuggestionChip |
Input |
| TopAppBar |
TopAppBar, CenterAlignedTopAppBar, LargeTopAppBar |
Navigation |
| Navigation Bar |
NavigationBar {} |
Navigation |
| Navigation Rail |
NavigationRail {} |
Navigation |
| Navigation Drawer |
ModalNavigationDrawer {} |
Navigation |
| Tabs |
TabRow {}, ScrollableTabRow {} |
Navigation |
Full Compose API + examples: references/component-catalog.md
AppTheme Setup (Quick Start)
@Composable
fun AppTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is Android 12+ only — always falls back to static on Desktop
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColorScheme
else -> LightColorScheme
}
MaterialTheme(
colorScheme = colorScheme,
typography = AppTypography,
shapes = AppShapes,
content = content
)
}
For Compose Multiplatform Desktop: Remove Build.VERSION_SDK_INT check and always use
LightColorScheme / DarkColorScheme. Dynamic color has no JVM equivalent.
Full theming guide: references/theming-and-dynamic-color.md
Core Rules
- Never hardcode
Color(0xFF...) in composables — always MaterialTheme.colorScheme.*
- Never inline
fontSize, fontFamily, fontWeight — always MaterialTheme.typography.*
- Never inline
RoundedCornerShape(12.dp) — always MaterialTheme.shapes.*
- Always wrap content in
AppTheme at the root — never in individual screens
- Always support dark mode — test every screen with
isSystemInDarkTheme()
- Always use
Scaffold — it handles topBar, bottomBar, FAB, snackbarHost, padding
- Minimum touch target: 48×48dp for all interactive elements
- Spacing tokens: always multiples of 4dp — define a
Dimens object
- For Desktop/JVM: always use static color schemes — no dynamic color API on JVM
MD3 Compliance Audit
When the user asks for an audit, a compliance check, or passes code / a screen name
with the audit argument, run a full MD3 compliance report using the template below.
How to trigger
audit [screen name or paste code here]
audit HomeScreen
audit <paste composable code>
check this screen against material 3
run md3 audit
Audit Process
- Scan the target — read the provided code or ask the user to paste the composable(s) to audit.
- Check each category below in order.
- Output the report using the exact format specified.
- Offer fixes — for every ❌ or ⚠️, provide the corrected Compose code snippet inline.
Audit Report Format
Output the report in this exact structure:
╔══════════════════════════════════════════════════════════╗
║ MD3 COMPLIANCE AUDIT — [Screen/File Name] ║
║ KMP / Compose Multiplatform Edition ║
╚══════════════════════════════════════════════════════════╝
Score: [X / 10] Grade: [A / B / C / D / F]
┌─────────────────────────────────────────────────────────┐
│ CATEGORY RESULTS │
└─────────────────────────────────────────────────────────┘
[✅ / ⚠️ / ❌] COLOR SYSTEM [PASS / WARN / FAIL]
[✅ / ⚠️ / ❌] TYPOGRAPHY [PASS / WARN / FAIL]
[✅ / ⚠️ / ❌] SHAPE [PASS / WARN / FAIL]
[✅ / ⚠️ / ❌] SPACING [PASS / WARN / FAIL]
[✅ / ⚠️ / ❌] ELEVATION [PASS / WARN / FAIL]
[✅ / ⚠️ / ❌] COMPONENTS [PASS / WARN / FAIL]
[✅ / ⚠️ / ❌] LAYOUT & ADAPTIVE [PASS / WARN / FAIL]
[✅ / ⚠️ / ❌] NAVIGATION [PASS / WARN / FAIL]
[✅ / ⚠️ / ❌] MOTION & ANIMATION [PASS / WARN / FAIL]
[✅ / ⚠️ / ❌] DARK MODE [PASS / WARN / FAIL]
[✅ / ⚠️ / ❌] ACCESSIBILITY [PASS / WARN / FAIL]
[✅ / ⚠️ / ❌] THEMING SETUP [PASS / WARN / FAIL]
┌─────────────────────────────────────────────────────────┐
│ FINDINGS │
└─────────────────────────────────────────────────────────┘
[findings listed per category — see check rules below]
┌─────────────────────────────────────────────────────────┐
│ FIXES │
└─────────────────────────────────────────────────────────┘
[corrected code snippets for every ❌ and ⚠️]
Audit Check Rules — Per Category
1. COLOR SYSTEM
| Check |
Pass condition |
Fail condition |
| No hardcoded colors in composables |
MaterialTheme.colorScheme.* used |
Color(0xFF…) literal in UI code |
| No swapped semantic roles |
error used for errors only |
error used for success/warning |
| Both schemes defined |
LightColorScheme + DarkColorScheme exist |
Only one scheme present |
| Dynamic color has static fallback |
if (dynamicColor && SDK >= S) with else branch |
Dynamic color used unconditionally |
Extended colors use CompositionLocal |
LocalExtendedColors pattern |
Raw color passed as parameter |
| Desktop: no dynamic color API |
jvmMain uses static scheme |
dynamicDarkColorScheme() in jvmMain |
2. TYPOGRAPHY
| Check |
Pass condition |
Fail condition |
| No inline font sizes |
MaterialTheme.typography.* used |
fontSize = 16.sp inline |
| No inline font weights |
MaterialTheme.typography.* used |
fontWeight = FontWeight.Bold inline |
Custom font loaded via Res.font.* |
Font(Res.font.*) for KMP |
Hardcoded path or fontFamily literal |
| All 15 styles defined if custom typography |
Typography(displayLarge = …, labelSmall = …) |
Missing styles in custom Typography |
Body text uses bodyLarge/bodyMedium |
Correct role applied |
displayLarge on body copy |
3. SHAPE
| Check |
Pass condition |
Fail condition |
No inline RoundedCornerShape |
MaterialTheme.shapes.* used |
RoundedCornerShape(12.dp) inline |
| Shape token matches component |
Cards use shapes.medium, FABs use shapes.extraLarge |
FAB with shapes.small |
Custom shapes defined in AppShapes |
val AppShapes = Shapes(…) in theme |
Shape overrides scattered in UI |
4. SPACING
| Check |
Pass condition |
Fail condition |
Spacing uses Dimens object |
Dimens.md, Dimens.lg, etc. |
Scattered 16.dp, 24.dp literals |
| Values are multiples of 4dp |
4, 8, 12, 16, 24, 32, 48dp |
15.dp, 7.dp, 11.dp literals |
| Screen margins consistent |
Dimens.screenHorizontal used |
Mixed margin values per screen |
5. ELEVATION
| Check |
Pass condition |
Fail condition |
| Tonal elevation used |
tonalElevation / CardDefaults.elevatedCardElevation() |
Modifier.shadow(8.dp) for depth |
| Shadow used only for busy backgrounds |
Rare Modifier.shadow with clear reason |
Shadows on all cards for styling |
6. COMPONENTS
| Check |
Pass condition |
Fail condition |
Only one Button (filled) per section |
Single primary action |
Multiple filled buttons per section |
FAB in Scaffold.floatingActionButton |
Scaffold(floatingActionButton = {…}) |
FAB positioned manually with Box |
Scaffold used on every screen |
Scaffold {} wraps each screen |
No Scaffold, manual layout |
AlertDialog for destructive actions |
confirmButton + dismissButton both present |
No dismiss option on destructive dialog |
Lists use ListItem |
ListItem(headlineContent = …) |
Custom Row replacing ListItem |
| Buttons use correct emphasis hierarchy |
Filled → Tonal → Elevated → Outlined → Text |
Multiple filled buttons, no hierarchy |
7. LAYOUT & ADAPTIVE
| Check |
Pass condition |
Fail condition |
WindowSizeClass used |
calculateWindowSizeClass() or currentWindowAdaptiveInfo() |
Fixed-width if (isTablet) hack |
| No hardcoded breakpoints |
WindowWidthSizeClass.* enum |
if (width > 600.dp) check |
| Canonical layout pattern used |
Feed / List-Detail / Supporting Pane |
None of the canonical patterns applied |
| Edge-to-edge enabled |
enableEdgeToEdge() in Activity |
Status bar not handled |
WindowInsets applied |
Modifier.statusBarsPadding() or Scaffold |
Content hidden behind system bars |
| Adaptive API used for list-detail |
NavigableListDetailPaneScaffold |
Manual Row reimplementing list-detail |
8. NAVIGATION
| Check |
Pass condition |
Fail condition |
| Nav component matches window size |
NavigationBar on compact, NavigationRail on medium, drawer on expanded |
Bottom nav on tablet |
| Bottom nav has 3–5 items |
Destination count in range |
2 or 6+ items in NavigationBar |
launchSingleTop = true |
Present on all nav clicks |
Duplicate back-stack entries possible |
saveState + restoreState |
Present on nav clicks |
Tab scroll position lost |
| Type-safe routes |
@Serializable objects/classes |
String literal routes |
NavController not in ViewModel |
Navigate via UiEffect |
navController injected into ViewModel |
9. MOTION & ANIMATION
| Check |
Pass condition |
Fail condition |
| Easing matches direction |
Entering: EmphasizedDecelerate, Leaving: EmphasizedAccelerate |
Symmetric easing for enter/exit |
animate*AsState has label = |
label = "colorAnimation" present |
Missing label parameter |
| Duration ≤ 500ms screen, ≤ 300ms component |
Within limits |
tween(800ms) on a button |
| Spring for interactions, tween for transitions |
spring() on drag/toggle, tween() on nav |
tween() on swipe gesture |
10. DARK MODE
| Check |
Pass condition |
Fail condition |
isSystemInDarkTheme() wired to theme |
darkTheme = isSystemInDarkTheme() |
Hard-coded darkTheme = false |
| Both schemes tested |
Code has DarkColorScheme defined |
Only LightColorScheme present |
@Preview(uiMode = UI_MODE_NIGHT_YES) on previews |
Both light and dark previews |
Only light mode previews |
11. ACCESSIBILITY
| Check |
Pass condition |
Fail condition |
| Touch targets ≥ 48×48dp |
Icons/buttons have Modifier.size(48.dp) or larger |
Modifier.size(24.dp) as only modifier on clickable |
Icon-only buttons have contentDescription |
Non-null description |
contentDescription = null on icon button |
| Contrast ratio ≥ 4.5:1 (normal text) |
M3 baseline palette used |
Custom palette not validated |
| No color-only state communication |
Icon/text also changes state |
Only color changes for selected state |
| Semantic roles applied |
Modifier.semantics { role = Role.Button } where needed |
Custom clickable without role |
12. THEMING SETUP
| Check |
Pass condition |
Fail condition |
Single MaterialTheme call at root |
AppTheme wraps root composable only |
MaterialTheme called in individual screens |
AppTheme has darkTheme + dynamicColor params |
Both parameters present |
Theme has no parameters |
AppTypography, AppShapes defined |
Separate files in theme/ |
Defaults used (MaterialTheme() with no args) |
Desktop uses expect/actual for theme |
rememberColorScheme split across source sets |
Android-only dynamic color call in commonMain |
Scoring
| Score |
Grade |
Meaning |
| 10 / 10 |
A |
Full MD3 compliance — production ready |
| 8–9 / 10 |
B |
Minor warnings — good with small fixes |
| 6–7 / 10 |
C |
Several violations — needs attention |
| 4–5 / 10 |
D |
Major issues — significant rework needed |
| 0–3 / 10 |
F |
Critical violations — MD3 not followed |
Each category scores 1 point: ✅ PASS = 1pt, ⚠️ WARN = 0.5pt, ❌ FAIL = 0pt.
Round to nearest 0.5.
Reference Files
| File |
Contents |
| references/color-system.md |
All 29 color roles, light/dark schemes, dynamic color, custom extensions |
| references/theming-and-dynamic-color.md |
AppTheme setup, dynamic color, KMP (Android+Desktop) theme split |
| references/typography-and-shape.md |
Full 15-style type scale, font setup, shape tokens, elevation, motion |
| references/component-catalog.md |
All 30+ components with Compose API + code examples |
| references/layout-and-responsive.md |
WindowSizeClass, adaptive scaffolds, canonical layouts, spacing tokens |
| references/navigation-patterns.md |
NavBar, Rail, Drawer, Tabs — when to use each, Compose wiring |
Dependencies
⚠️ Deprecation: plugin accessor shorthands
The compose.material3, compose.ui, compose.foundation shorthand accessors previously
provided by the Compose Multiplatform Gradle plugin are deprecated as of CMP 1.10.0-beta01.
| Old (deprecated) |
New (explicit libs entry) |
implementation(compose.material3) |
implementation(libs.compose.material3) |
implementation(compose.ui) |
implementation(libs.compose.ui) |
implementation(compose.foundation) |
implementation(libs.compose.foundation) |
⚠️ Breaking: material-icons-core is no longer transitive (since CMP 1.8.2)
Starting with CMP 1.8.2, the implicit dependency on material-icons-core was removed.
If your project uses Icons.Default.* or any Material icon, add it explicitly.
gradle/libs.versions.toml
[versions]
kotlin = "2.1.21"
agp = "8.10.0"
composeMultiplatform = "1.8.2" # org.jetbrains.compose plugin version
coroutines = "1.10.2"
lifecycle = "2.9.0"
[libraries]
# ✅ Correct module for commonMain
# The CMP plugin maps this → androidx.compose.material3 on Android automatically
compose-material3 = { module = "org.jetbrains.compose.material3:material3", version.ref = "composeMultiplatform" }
compose-runtime = { module = "org.jetbrains.compose.runtime:runtime", version.ref = "composeMultiplatform" }
compose-foundation = { module = "org.jetbrains.compose.foundation:foundation", version.ref = "composeMultiplatform" }
compose-ui = { module = "org.jetbrains.compose.ui:ui", version.ref = "composeMultiplatform" }
# Declare explicitly — no longer a transitive dep since CMP 1.8.2
compose-material-icons-core = { module = "org.jetbrains.compose.material:material-icons-core", version.ref = "composeMultiplatform" }
compose-ui-tooling-preview = { module = "org.jetbrains.compose.ui:ui-tooling-preview", version.ref = "composeMultiplatform" }
[plugins]
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
androidApplication = { id = "com.android.application", version.ref = "agp" }
composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "composeMultiplatform" }
composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
composeApp/build.gradle.kts
kotlin {
sourceSets {
commonMain.dependencies {
// ✅ Use libs.* — NOT the deprecated compose.* plugin accessors
implementation(libs.compose.runtime)
implementation(libs.compose.foundation)
implementation(libs.compose.ui)
implementation(libs.compose.material3)
implementation(libs.compose.material.icons.core) // explicit since CMP 1.8.2
}
androidMain.dependencies {
implementation(libs.compose.ui.tooling.preview)
}
}
}
Module mapping (how it works)
| Source set |
Module in toml |
What Gradle actually resolves |
commonMain |
org.jetbrains.compose.material3:material3 |
JetBrains multiplatform artifact |
androidMain (via CMP plugin metadata) |
same toml entry |
androidx.compose.material3:material3 |
jvmMain (Desktop) |
same toml entry |
JetBrains desktop artifact |
You never need to manually switch to androidx.compose.material3 in build files —
the CMP Gradle plugin metadata handles the platform mapping transparently.
Official Docs
1---2name: material3-design-system3description: Comprehensive Material 3 (Material You) design system skill for Jetpack Compose and Compose Multiplatform (KMP). Covers color tokens, typography, shape, 30+ components, adaptive layout, navigation patterns, dynamic color, dark mode, motion/animation, and accessibility. Compose-first — no web or Flutter code.4---56# Material Design 3 — KMP / Compose Multiplatform Skill78This skill guides implementation of Google's **Material Design 3 (MD3 / Material You)** using9**Jetpack Compose** and **Compose Multiplatform** for KMP projects (Android + Desktop).1011> **Attribution**: This skill is adapted from and gives credit to12> **[hamen/material-3-skill](https://github.com/hamen/material-3-skill)** by Hamen.13> The original skill covers web and Flutter targets. This KMP edition strips all web/CSS/Flutter14> patterns and replaces them with pure Compose Multiplatform APIs, adds the audit report system,15> and extends the reference set for adaptive layout, navigation, and versioning.1617> **Scope**: Compose-only. All examples use `androidx.compose.material3` APIs — the same API18> surface is available in KMP `commonMain` via `org.jetbrains.compose.material3:material3`19> (declared explicitly in `libs.versions.toml`). No web CSS, no `@material/web` elements, no Flutter.2021---2223## MD3 Philosophy2425| Principle | What it means in Compose |26|---|---|27| **Personal** | Dynamic color from user wallpaper (Android 12+). Static fallback for Desktop. |28| **Adaptive** | `WindowSizeClass` drives layout changes across compact → expanded screens. |29| **Expressive** | Spring-based motion, shape morphing, emphasized typography. |3031### Google I/O 2026 Key Updates3233- **Compose-first on Android**: For all new Android work, use `androidx.compose.material3`.34- **Expressive layout scaffold**: Design screens to adapt across mobile, desktop, foldables. Use `Material3Adaptive` scaffold APIs.35- **8dp spacing system**: Define spacing as tokens — never scatter raw `Dp` literals.36- **New expressive components**: Lists, menus, search, and search app bars have refreshed expressive guidance; check your Material3 BOM for expressive variants.3738---3940## Design Token System4142All MD3 values come through `MaterialTheme`. **Never hardcode** raw values inline.4344| Token category | Access in Compose |45|---|---|46| Color | `MaterialTheme.colorScheme.*` |47| Typography | `MaterialTheme.typography.*` |48| Shape | `MaterialTheme.shapes.*` |49| Spacing | Define a `Dimens` object (no built-in API) |5051---5253## Decision Tree5455```56What are you building?5758Full app scaffold → AppTheme setup + references/theming-and-dynamic-color.md59Single component → references/component-catalog.md60Custom color theme → references/color-system.md61Typography / fonts → references/typography-and-shape.md62Navigation structure → references/navigation-patterns.md63Adaptive layout → references/layout-and-responsive.md64```6566---6768## Color Token Summary6970Full details in [references/color-system.md](references/color-system.md).7172### Key Roles (Compose token → usage)7374| Role | Token | Primary Usage |75|---|---|---|76| Primary | `colorScheme.primary` | FAB, key buttons, active states |77| On Primary | `colorScheme.onPrimary` | Text/icons on primary |78| Primary Container | `colorScheme.primaryContainer` | Tonal buttons, selected chips |79| On Primary Container | `colorScheme.onPrimaryContainer` | Text on primary container |80| Secondary | `colorScheme.secondary` | Less prominent accents, filters |81| Secondary Container | `colorScheme.secondaryContainer` | Recessive fills |82| Tertiary | `colorScheme.tertiary` | Contrasting accent sections |83| Surface | `colorScheme.surface` | Cards, sheets, menus |84| Surface Container | `colorScheme.surfaceContainer` | Navigation areas |85| On Surface | `colorScheme.onSurface` | Body text, icons |86| On Surface Variant | `colorScheme.onSurfaceVariant` | Placeholder, helper text |87| Outline | `colorScheme.outline` | Input borders, dividers |88| Error | `colorScheme.error` | Error states |8990---9192## Typography Token Summary9394Full details in [references/typography-and-shape.md](references/typography-and-shape.md).9596| Category | Styles | Usage |97|---|---|---|98| Display | L / M / S | Hero text, large numbers |99| Headline | L / M / S | Screen/section headers |100| Title | L / M / S | Toolbar titles, card headers |101| Body | L / M / S | Paragraph text, descriptions |102| Label | L / M / S | Buttons, chips, captions |103104```kotlin105// ✅ Always via MaterialTheme106Text("Title", style = MaterialTheme.typography.titleLarge)107Text("Body", style = MaterialTheme.typography.bodyMedium)108109// ❌ Never inline110Text("Title", fontSize = 22.sp, fontWeight = FontWeight.Normal)111```112113---114115## Shape Token Summary116117| Token | Corner Radius | Typical Components |118|---|---|---|119| `shapes.extraSmall` | 4dp | Chips, snackbars |120| `shapes.small` | 8dp | Text fields, menus |121| `shapes.medium` | 12dp | Cards |122| `shapes.large` | 16dp | FABs, nav drawer |123| `shapes.extraLarge` | 28dp | Dialogs, bottom sheets |124125---126127## Elevation128129MD3 communicates depth through **tonal surface color**, not drop shadows.130131| Level | Compose API | Tonal Offset | Use |132|---|---|---|---|133| 0 | `Elevation.Level0` / `0.dp` | None | Flat surfaces at rest |134| 1 | `Elevation.Level1` / `1.dp` | +5% primary | Elevated cards |135| 2 | `Elevation.Level2` / `3.dp` | +8% primary | Menus, nav bar |136| 3 | `Elevation.Level3` / `6.dp` | +11% primary | FAB, dialogs |137138```kotlin139// Cards respect tonal elevation automatically via surfaceTonalElevation140ElevatedCard(elevation = CardDefaults.elevatedCardElevation(defaultElevation = 6.dp)) { }141```142143---144145## Motion Summary146147Full details in [references/typography-and-shape.md](references/typography-and-shape.md) §Motion.148149| Easing | Compose | Usage |150|---|---|---|151| Emphasized | `CubicBezierEasing(0.2f, 0f, 0f, 1f)` | Elements staying on screen |152| Emphasized Decelerate | `CubicBezierEasing(0.05f, 0.7f, 0.1f, 1f)` | Entering screen |153| Emphasized Accelerate | `CubicBezierEasing(0.3f, 0f, 0.8f, 0.15f)` | Leaving screen |154| Standard | `FastOutSlowInEasing` | Utility animations |155156### Standard Durations157158| Token | Duration | Usage |159|---|---|---|160| Short | 100–200ms | Icon/color state changes |161| Medium | 300–400ms | Component expand/collapse |162| Long | 400–500ms | Screen-level transitions |163164---165166## Component Quick Reference167168| Component | Compose API | Category |169|---|---|---|170| Button (Filled) | `Button {}` | Actions |171| Button (Tonal) | `FilledTonalButton {}` | Actions |172| Button (Outlined) | `OutlinedButton {}` | Actions |173| Button (Text) | `TextButton {}` | Actions |174| FAB | `FloatingActionButton {}` | Actions |175| Extended FAB | `ExtendedFloatingActionButton {}` | Actions |176| Icon Button | `IconButton {}`, `FilledIconButton {}` | Actions |177| Segmented Button | `SegmentedButton {}` | Actions |178| Card | `Card {}`, `ElevatedCard {}`, `OutlinedCard {}` | Containment |179| Dialog | `AlertDialog {}`, `Dialog {}` | Containment |180| Bottom Sheet | `ModalBottomSheet {}` | Sheets |181| Snackbar | `SnackbarHost {}` | Communication |182| Progress | `CircularProgressIndicator()`, `LinearProgressIndicator()` | Communication |183| Badge | `BadgedBox {}` | Communication |184| Checkbox | `Checkbox()` | Input |185| RadioButton | `RadioButton()` | Input |186| Switch | `Switch()` | Input |187| Slider | `Slider()`, `RangeSlider()` | Input |188| TextField | `TextField()`, `OutlinedTextField()` | Input |189| Chips | `FilterChip`, `AssistChip`, `InputChip`, `SuggestionChip` | Input |190| TopAppBar | `TopAppBar`, `CenterAlignedTopAppBar`, `LargeTopAppBar` | Navigation |191| Navigation Bar | `NavigationBar {}` | Navigation |192| Navigation Rail | `NavigationRail {}` | Navigation |193| Navigation Drawer | `ModalNavigationDrawer {}` | Navigation |194| Tabs | `TabRow {}`, `ScrollableTabRow {}` | Navigation |195196Full Compose API + examples: [references/component-catalog.md](references/component-catalog.md)197198---199200## AppTheme Setup (Quick Start)201202```kotlin203@Composable204fun AppTheme(205 darkTheme: Boolean = isSystemInDarkTheme(),206 // Dynamic color is Android 12+ only — always falls back to static on Desktop207 dynamicColor: Boolean = true,208 content: @Composable () -> Unit209) {210 val colorScheme = when {211 dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {212 val context = LocalContext.current213 if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)214 }215 darkTheme -> DarkColorScheme216 else -> LightColorScheme217 }218219 MaterialTheme(220 colorScheme = colorScheme,221 typography = AppTypography,222 shapes = AppShapes,223 content = content224 )225}226```227228> For **Compose Multiplatform Desktop**: Remove `Build.VERSION_SDK_INT` check and always use229> `LightColorScheme` / `DarkColorScheme`. Dynamic color has no JVM equivalent.230231Full theming guide: [references/theming-and-dynamic-color.md](references/theming-and-dynamic-color.md)232233---234235## Core Rules236237- **Never** hardcode `Color(0xFF...)` in composables — always `MaterialTheme.colorScheme.*`238- **Never** inline `fontSize`, `fontFamily`, `fontWeight` — always `MaterialTheme.typography.*`239- **Never** inline `RoundedCornerShape(12.dp)` — always `MaterialTheme.shapes.*`240- **Always** wrap content in `AppTheme` at the root — never in individual screens241- **Always** support dark mode — test every screen with `isSystemInDarkTheme()`242- **Always** use `Scaffold` — it handles `topBar`, `bottomBar`, `FAB`, `snackbarHost`, padding243- Minimum touch target: **48×48dp** for all interactive elements244- Spacing tokens: **always multiples of 4dp** — define a `Dimens` object245- For Desktop/JVM: always use static color schemes — no dynamic color API on JVM246247---248249## MD3 Compliance Audit250251When the user asks for an **audit**, a **compliance check**, or passes code / a screen name252with the `audit` argument, run a full MD3 compliance report using the template below.253254### How to trigger255256```257audit [screen name or paste code here]258audit HomeScreen259audit <paste composable code>260check this screen against material 3261run md3 audit262```263264### Audit Process2652661. **Scan the target** — read the provided code or ask the user to paste the composable(s) to audit.2672. **Check each category** below in order.2683. **Output the report** using the exact format specified.2694. **Offer fixes** — for every ❌ or ⚠️, provide the corrected Compose code snippet inline.270271---272273### Audit Report Format274275Output the report in this exact structure:276277```278╔══════════════════════════════════════════════════════════╗279║ MD3 COMPLIANCE AUDIT — [Screen/File Name] ║280║ KMP / Compose Multiplatform Edition ║281╚══════════════════════════════════════════════════════════╝282283Score: [X / 10] Grade: [A / B / C / D / F]284285┌─────────────────────────────────────────────────────────┐286│ CATEGORY RESULTS │287└─────────────────────────────────────────────────────────┘288289[✅ / ⚠️ / ❌] COLOR SYSTEM [PASS / WARN / FAIL]290[✅ / ⚠️ / ❌] TYPOGRAPHY [PASS / WARN / FAIL]291[✅ / ⚠️ / ❌] SHAPE [PASS / WARN / FAIL]292[✅ / ⚠️ / ❌] SPACING [PASS / WARN / FAIL]293[✅ / ⚠️ / ❌] ELEVATION [PASS / WARN / FAIL]294[✅ / ⚠️ / ❌] COMPONENTS [PASS / WARN / FAIL]295[✅ / ⚠️ / ❌] LAYOUT & ADAPTIVE [PASS / WARN / FAIL]296[✅ / ⚠️ / ❌] NAVIGATION [PASS / WARN / FAIL]297[✅ / ⚠️ / ❌] MOTION & ANIMATION [PASS / WARN / FAIL]298[✅ / ⚠️ / ❌] DARK MODE [PASS / WARN / FAIL]299[✅ / ⚠️ / ❌] ACCESSIBILITY [PASS / WARN / FAIL]300[✅ / ⚠️ / ❌] THEMING SETUP [PASS / WARN / FAIL]301302┌─────────────────────────────────────────────────────────┐303│ FINDINGS │304└─────────────────────────────────────────────────────────┘305306[findings listed per category — see check rules below]307308┌─────────────────────────────────────────────────────────┐309│ FIXES │310└─────────────────────────────────────────────────────────┘311312[corrected code snippets for every ❌ and ⚠️]313```314315---316317### Audit Check Rules — Per Category318319#### 1. COLOR SYSTEM320321| Check | Pass condition | Fail condition |322|---|---|---|323| No hardcoded colors in composables | `MaterialTheme.colorScheme.*` used | `Color(0xFF…)` literal in UI code |324| No swapped semantic roles | `error` used for errors only | `error` used for success/warning |325| Both schemes defined | `LightColorScheme` + `DarkColorScheme` exist | Only one scheme present |326| Dynamic color has static fallback | `if (dynamicColor && SDK >= S)` with else branch | Dynamic color used unconditionally |327| Extended colors use `CompositionLocal` | `LocalExtendedColors` pattern | Raw color passed as parameter |328| Desktop: no dynamic color API | `jvmMain` uses static scheme | `dynamicDarkColorScheme()` in `jvmMain` |329330#### 2. TYPOGRAPHY331332| Check | Pass condition | Fail condition |333|---|---|---|334| No inline font sizes | `MaterialTheme.typography.*` used | `fontSize = 16.sp` inline |335| No inline font weights | `MaterialTheme.typography.*` used | `fontWeight = FontWeight.Bold` inline |336| Custom font loaded via `Res.font.*` | `Font(Res.font.*)` for KMP | Hardcoded path or `fontFamily` literal |337| All 15 styles defined if custom typography | `Typography(displayLarge = …, labelSmall = …)` | Missing styles in custom `Typography` |338| Body text uses `bodyLarge`/`bodyMedium` | Correct role applied | `displayLarge` on body copy |339340#### 3. SHAPE341342| Check | Pass condition | Fail condition |343|---|---|---|344| No inline `RoundedCornerShape` | `MaterialTheme.shapes.*` used | `RoundedCornerShape(12.dp)` inline |345| Shape token matches component | Cards use `shapes.medium`, FABs use `shapes.extraLarge` | FAB with `shapes.small` |346| Custom shapes defined in `AppShapes` | `val AppShapes = Shapes(…)` in theme | Shape overrides scattered in UI |347348#### 4. SPACING349350| Check | Pass condition | Fail condition |351|---|---|---|352| Spacing uses `Dimens` object | `Dimens.md`, `Dimens.lg`, etc. | Scattered `16.dp`, `24.dp` literals |353| Values are multiples of 4dp | 4, 8, 12, 16, 24, 32, 48dp | 15.dp, 7.dp, 11.dp literals |354| Screen margins consistent | `Dimens.screenHorizontal` used | Mixed margin values per screen |355356#### 5. ELEVATION357358| Check | Pass condition | Fail condition |359|---|---|---|360| Tonal elevation used | `tonalElevation` / `CardDefaults.elevatedCardElevation()` | `Modifier.shadow(8.dp)` for depth |361| Shadow used only for busy backgrounds | Rare `Modifier.shadow` with clear reason | Shadows on all cards for styling |362363#### 6. COMPONENTS364365| Check | Pass condition | Fail condition |366|---|---|---|367| Only one `Button` (filled) per section | Single primary action | Multiple filled buttons per section |368| FAB in `Scaffold.floatingActionButton` | `Scaffold(floatingActionButton = {…})` | FAB positioned manually with `Box` |369| `Scaffold` used on every screen | `Scaffold {}` wraps each screen | No `Scaffold`, manual layout |370| `AlertDialog` for destructive actions | `confirmButton` + `dismissButton` both present | No dismiss option on destructive dialog |371| Lists use `ListItem` | `ListItem(headlineContent = …)` | Custom `Row` replacing `ListItem` |372| Buttons use correct emphasis hierarchy | Filled → Tonal → Elevated → Outlined → Text | Multiple filled buttons, no hierarchy |373374#### 7. LAYOUT & ADAPTIVE375376| Check | Pass condition | Fail condition |377|---|---|---|378| `WindowSizeClass` used | `calculateWindowSizeClass()` or `currentWindowAdaptiveInfo()` | Fixed-width `if (isTablet)` hack |379| No hardcoded breakpoints | `WindowWidthSizeClass.*` enum | `if (width > 600.dp)` check |380| Canonical layout pattern used | Feed / List-Detail / Supporting Pane | None of the canonical patterns applied |381| Edge-to-edge enabled | `enableEdgeToEdge()` in Activity | Status bar not handled |382| `WindowInsets` applied | `Modifier.statusBarsPadding()` or `Scaffold` | Content hidden behind system bars |383| Adaptive API used for list-detail | `NavigableListDetailPaneScaffold` | Manual `Row` reimplementing list-detail |384385#### 8. NAVIGATION386387| Check | Pass condition | Fail condition |388|---|---|---|389| Nav component matches window size | `NavigationBar` on compact, `NavigationRail` on medium, drawer on expanded | Bottom nav on tablet |390| Bottom nav has 3–5 items | Destination count in range | 2 or 6+ items in `NavigationBar` |391| `launchSingleTop = true` | Present on all nav clicks | Duplicate back-stack entries possible |392| `saveState + restoreState` | Present on nav clicks | Tab scroll position lost |393| Type-safe routes | `@Serializable` objects/classes | String literal routes |394| `NavController` not in ViewModel | Navigate via `UiEffect` | `navController` injected into ViewModel |395396#### 9. MOTION & ANIMATION397398| Check | Pass condition | Fail condition |399|---|---|---|400| Easing matches direction | Entering: `EmphasizedDecelerate`, Leaving: `EmphasizedAccelerate` | Symmetric easing for enter/exit |401| `animate*AsState` has `label =` | `label = "colorAnimation"` present | Missing `label` parameter |402| Duration ≤ 500ms screen, ≤ 300ms component | Within limits | `tween(800ms)` on a button |403| Spring for interactions, tween for transitions | `spring()` on drag/toggle, `tween()` on nav | `tween()` on swipe gesture |404405#### 10. DARK MODE406407| Check | Pass condition | Fail condition |408|---|---|---|409| `isSystemInDarkTheme()` wired to theme | `darkTheme = isSystemInDarkTheme()` | Hard-coded `darkTheme = false` |410| Both schemes tested | Code has `DarkColorScheme` defined | Only `LightColorScheme` present |411| `@Preview(uiMode = UI_MODE_NIGHT_YES)` on previews | Both light and dark previews | Only light mode previews |412413#### 11. ACCESSIBILITY414415| Check | Pass condition | Fail condition |416|---|---|---|417| Touch targets ≥ 48×48dp | Icons/buttons have `Modifier.size(48.dp)` or larger | `Modifier.size(24.dp)` as only modifier on clickable |418| Icon-only buttons have `contentDescription` | Non-null description | `contentDescription = null` on icon button |419| Contrast ratio ≥ 4.5:1 (normal text) | M3 baseline palette used | Custom palette not validated |420| No color-only state communication | Icon/text also changes state | Only color changes for selected state |421| Semantic roles applied | `Modifier.semantics { role = Role.Button }` where needed | Custom clickable without role |422423#### 12. THEMING SETUP424425| Check | Pass condition | Fail condition |426|---|---|---|427| Single `MaterialTheme` call at root | `AppTheme` wraps root composable only | `MaterialTheme` called in individual screens |428| `AppTheme` has `darkTheme` + `dynamicColor` params | Both parameters present | Theme has no parameters |429| `AppTypography`, `AppShapes` defined | Separate files in `theme/` | Defaults used (`MaterialTheme()` with no args) |430| Desktop uses `expect`/`actual` for theme | `rememberColorScheme` split across source sets | Android-only dynamic color call in `commonMain` |431432---433434### Scoring435436| Score | Grade | Meaning |437|---|---|---|438| 10 / 10 | **A** | Full MD3 compliance — production ready |439| 8–9 / 10 | **B** | Minor warnings — good with small fixes |440| 6–7 / 10 | **C** | Several violations — needs attention |441| 4–5 / 10 | **D** | Major issues — significant rework needed |442| 0–3 / 10 | **F** | Critical violations — MD3 not followed |443444Each category scores 1 point: **✅ PASS = 1pt**, **⚠️ WARN = 0.5pt**, **❌ FAIL = 0pt**.445Round to nearest 0.5.446447---448449## Reference Files450451| File | Contents |452|---|---|453| [references/color-system.md](references/color-system.md) | All 29 color roles, light/dark schemes, dynamic color, custom extensions |454| [references/theming-and-dynamic-color.md](references/theming-and-dynamic-color.md) | AppTheme setup, dynamic color, KMP (Android+Desktop) theme split |455| [references/typography-and-shape.md](references/typography-and-shape.md) | Full 15-style type scale, font setup, shape tokens, elevation, motion |456| [references/component-catalog.md](references/component-catalog.md) | All 30+ components with Compose API + code examples |457| [references/layout-and-responsive.md](references/layout-and-responsive.md) | WindowSizeClass, adaptive scaffolds, canonical layouts, spacing tokens |458| [references/navigation-patterns.md](references/navigation-patterns.md) | NavBar, Rail, Drawer, Tabs — when to use each, Compose wiring |459460---461462## Dependencies463464### ⚠️ Deprecation: plugin accessor shorthands465466The `compose.material3`, `compose.ui`, `compose.foundation` **shorthand accessors** previously467provided by the Compose Multiplatform Gradle plugin are **deprecated as of CMP 1.10.0-beta01**.468469| Old (deprecated) | New (explicit libs entry) |470|---|---|471| `implementation(compose.material3)` | `implementation(libs.compose.material3)` |472| `implementation(compose.ui)` | `implementation(libs.compose.ui)` |473| `implementation(compose.foundation)` | `implementation(libs.compose.foundation)` |474475### ⚠️ Breaking: `material-icons-core` is no longer transitive (since CMP 1.8.2)476477Starting with CMP 1.8.2, the implicit dependency on `material-icons-core` was removed.478If your project uses `Icons.Default.*` or any Material icon, add it **explicitly**.479480### `gradle/libs.versions.toml`481482```toml483[versions]484kotlin = "2.1.21"485agp = "8.10.0"486composeMultiplatform = "1.8.2" # org.jetbrains.compose plugin version487coroutines = "1.10.2"488lifecycle = "2.9.0"489490[libraries]491# ✅ Correct module for commonMain492# The CMP plugin maps this → androidx.compose.material3 on Android automatically493compose-material3 = { module = "org.jetbrains.compose.material3:material3", version.ref = "composeMultiplatform" }494compose-runtime = { module = "org.jetbrains.compose.runtime:runtime", version.ref = "composeMultiplatform" }495compose-foundation = { module = "org.jetbrains.compose.foundation:foundation", version.ref = "composeMultiplatform" }496compose-ui = { module = "org.jetbrains.compose.ui:ui", version.ref = "composeMultiplatform" }497# Declare explicitly — no longer a transitive dep since CMP 1.8.2498compose-material-icons-core = { module = "org.jetbrains.compose.material:material-icons-core", version.ref = "composeMultiplatform" }499compose-ui-tooling-preview = { module = "org.jetbrains.compose.ui:ui-tooling-preview", version.ref = "composeMultiplatform" }500501[plugins]502kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }503androidApplication = { id = "com.android.application", version.ref = "agp" }504composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "composeMultiplatform" }505composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }506```507508### `composeApp/build.gradle.kts`509510```kotlin511kotlin {512 sourceSets {513 commonMain.dependencies {514 // ✅ Use libs.* — NOT the deprecated compose.* plugin accessors515 implementation(libs.compose.runtime)516 implementation(libs.compose.foundation)517 implementation(libs.compose.ui)518 implementation(libs.compose.material3)519 implementation(libs.compose.material.icons.core) // explicit since CMP 1.8.2520 }521 androidMain.dependencies {522 implementation(libs.compose.ui.tooling.preview)523 }524 }525}526```527528### Module mapping (how it works)529530| Source set | Module in toml | What Gradle actually resolves |531|---|---|---|532| `commonMain` | `org.jetbrains.compose.material3:material3` | JetBrains multiplatform artifact |533| `androidMain` (via CMP plugin metadata) | same toml entry | `androidx.compose.material3:material3` |534| `jvmMain` (Desktop) | same toml entry | JetBrains desktop artifact |535536You **never** need to manually switch to `androidx.compose.material3` in build files —537the CMP Gradle plugin metadata handles the platform mapping transparently.538539---540541## Official Docs542543- [Material 3](https://m3.material.io/)544- [Compose Material 3](https://developer.android.com/develop/ui/compose/designsystems/material3)545- [M3 Theme Builder](https://material-foundation.github.io/material-theme-builder/)546- [Compose Multiplatform](https://www.jetbrains.com/compose-multiplatform/)547- [Material3 Adaptive](https://developer.android.com/jetpack/androidx/releases/compose-material3-adaptive)548- [CMP Release Notes](https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-multiplatform-releases.html)