android-design

This skill should be used when the user asks to "audit Android code", "review Jetpack Compose", "check Material Design compliance", "critique Compose design", "verify M3 guidelines", "audit Compose screen", "review Material 3", "check M3 compliance", "validate Android UI", "fix Compose anti-patterns", "improve Compose performance", "review Android screen", "audit Kotlin UI", "check touch targets", "verify accessibility Android", or mentions "Material Design 3", "M3", "Jetpack Compose", "Android UI", "Compose patterns", "recomposition", "Compose stability", "Android accessibility", "WCAG Android". This skill should also be used when the user reports symptoms like "Why is my list lagging?", "Scroll is jittery", "Make this look native", "The notch is covering my UI", "Build a settings screen", "My Compose is slow", "Recomposition issues", or "Screen feels sluggish". Provides comprehensive Material Design 3 specifications, Compose anti-patterns database, typography scale, color system, touch targets, and severity-based audit checklists.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "android-design" with this command: npx skills add moonlightbyte/kiln/moonlightbyte-kiln-android-design

Android Design Skill

Expert knowledge of Material Design 3 for Jetpack Compose applications. This skill enforces design excellence, accessibility compliance, and performance best practices.

Core Principles

1. Material Design 3 Foundation

  • Use semantic tokens, not hardcoded values
  • Apply dynamic color for Android 12+
  • Follow M3 shape scale (4dp extraSmall to 28dp extraLarge)
  • Use elevation levels (0dp to 5 levels)

2. Accessibility First

  • Minimum 48x48dp touch targets
  • 4.5:1 contrast ratio for normal text
  • Proper contentDescription on all interactive elements
  • Support fontScale up to 200%

3. Performance Stability

  • Mark data classes @Immutable or @Stable
  • Use remember and derivedStateOf correctly
  • Prefer LazyColumn over Column for lists
  • Provide stable keys for list items

Typography Scale (15 Styles)

TokenSizeLine HeightWeightUse Case
displayLarge57sp64sp400Hero headlines
displayMedium45sp52sp400Page titles
displaySmall36sp44sp400Section headers
headlineLarge32sp40sp400Card titles
headlineMedium28sp36sp400Dialog titles
headlineSmall24sp32sp400List headers
titleLarge22sp28sp400App bar titles
titleMedium16sp24sp500Tab labels
titleSmall14sp20sp500Chip labels
bodyLarge16sp24sp400Body text
bodyMedium14sp20sp400Secondary text
bodySmall12sp16sp400Captions
labelLarge14sp20sp500Buttons
labelMedium12sp16sp500Small buttons
labelSmall11sp16sp500Badges

Usage Pattern

// GOOD - Semantic tokens
Text(
    text = "Welcome",
    style = MaterialTheme.typography.headlineLarge
)

// BAD - Hardcoded values
Text(
    text = "Welcome",
    fontSize = 32.sp,
    fontWeight = FontWeight.Normal
)

Color System

Key Colors (5 Primary)

  1. Primary - Brand color, interactive elements
  2. Secondary - Less prominent UI
  3. Tertiary - Accents, special elements
  4. Error - Error states
  5. Neutral - Surfaces, backgrounds

Tonal Palettes (13 Tones)

Each key color generates: 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 99, 100

Usage Pattern

// GOOD - Semantic tokens
Surface(color = MaterialTheme.colorScheme.surface) {
    Text(
        text = "Hello",
        color = MaterialTheme.colorScheme.onSurface
    )
}

// BAD - Hardcoded colors
Surface(color = Color(0xFFFAFAFA)) {
    Text(
        text = "Hello",
        color = Color.Black
    )
}

Dynamic Color (Android 12+)

val colorScheme = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
    if (darkTheme) dynamicDarkColorScheme(context)
    else dynamicLightColorScheme(context)
} else {
    if (darkTheme) DarkColorScheme
    else LightColorScheme
}

Shape Scale

TokenRadiusUse Case
extraSmall4dpBadges, chips
small8dpSmall cards, buttons
medium12dpCards, dialogs
large16dpLarge sheets
extraLarge28dpFABs, full sheets

Touch Targets

Requirements

  • Minimum: 48x48dp (M3 requirement)
  • Recommended: 48x48dp with 8dp spacing
  • WCAG 2.2: 24x24 CSS px minimum (Level AA)

Implementation

// GOOD - Proper touch target
IconButton(
    onClick = { /* action */ },
    modifier = Modifier.minimumInteractiveComponentSize()
) {
    Icon(
        imageVector = Icons.Default.Close,
        contentDescription = "Close dialog"
    )
}

// BAD - No touch target guarantee
Icon(
    imageVector = Icons.Default.Close,
    modifier = Modifier
        .size(24.dp)
        .clickable { /* action */ }
)

Spacing Scale

Base unit: 4dp

SizedpUse Case
xs4dpInline spacing
sm8dpRelated elements
md12dpGroup spacing
lg16dpSection spacing
xl24dpMajor divisions
2xl32dpPage margins
3xl48dpLarge gaps
4xl64dpHero spacing

DO / DON'T

State Management

DO: Hoist state to appropriate level

@Composable
fun SearchScreen(viewModel: SearchViewModel = viewModel()) {
    val query by viewModel.searchQuery.collectAsState()
    SearchBar(query = query, onQueryChange = viewModel::updateQuery)
}

DON'T: Create state in leaf composables

@Composable
fun SearchBar() {
    var query by remember { mutableStateOf("") } // Can't test, can't share
    TextField(value = query, onValueChange = { query = it })
}

Modifiers

DO: Create modifiers outside composable body

private val cardModifier = Modifier
    .fillMaxWidth()
    .padding(16.dp)

@Composable
fun MyCard() {
    Card(modifier = cardModifier) { /* content */ }
}

DON'T: Create modifiers inside composition

@Composable
fun MyCard() {
    Card(
        modifier = Modifier // Creates new object every recomposition
            .fillMaxWidth()
            .padding(16.dp)
    ) { /* content */ }
}

Collections

DO: Use immutable collections for stable recomposition

@Immutable
data class UiState(
    val items: ImmutableList<Item> = persistentListOf()
)

DON'T: Use mutable collections in state

data class UiState(
    val items: List<Item> = emptyList() // Unstable!
)

LazyColumn Keys

DO: Provide stable keys

LazyColumn {
    items(items, key = { it.id }) { item ->
        ItemRow(item)
    }
}

DON'T: Rely on index-based keys

LazyColumn {
    items(items.size) { index -> // Breaks on reorder
        ItemRow(items[index])
    }
}

Severity Levels

When auditing, classify issues as:

LevelIconDescriptionExample
Critical🔴Blocks accessibility or causes crashesMissing contentDescription
Important🟠Violates M3 guidelines or hurts UXTouch target < 48dp
Warning🟡Suboptimal but functionalHardcoded color
Suggestion🔵Polish opportunitiesNon-standard spacing

References

For detailed specifications, see:

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

ios-design

No summary provided by upstream source.

Repository SourceNeeds Review
Security

Agent Task Status

Check whether named OpenClaw agents received formal task assignments and what execution status they reported. Use when auditing multi-agent orchestration, co...

Registry SourceRecently Updated
Security

Skill Reviewer

Use this skill to audit, review, or validate Claude Code skills (.md files in .claude/commands/). Invoke when user wants to check skill quality, cross-platfo...

Registry SourceRecently Updated