name: software-android-design
description: "Designs and audits native Android interfaces. Use when reviewing Compose layout, typography, color, motion, or adaptive patterns on a verified emulator build."
compatibility: Portable core. Works on Claude Code and Codex.
version: "1.2"
last_validated: 2026-07-11
Native Android Design
Use this skill for visual design decisions and design-focused audits in native Android apps. Prefer it when the user needs Material 3 aligned screen structure, current Android-native visual defaults, or a screenshot-to-fix loop after a fresh verified build/install/launch. If the request is really about greenfield scaffolding, Gradle build loops, module selection, or broader Android implementation workflow, route to software-mobile and return here once the question becomes visual structure or design quality.
Quick Reference
| Design concern | Default | Notes |
|---|
| Type | MaterialTheme.typography | Display → Headline → Title → Body → Label scale |
| Color | MaterialTheme.colorScheme | primary, secondary, tertiary, surface, error, and on* variants |
| Shape | MaterialTheme.shapes | ExtraSmall→ExtraLarge; RoundedCornerShape |
| Dynamic color | dynamicDarkColorScheme / dynamicLightColorScheme | Android 12+; static seed palette on older APIs |
| Bottom nav | NavigationBar 3–5 destinations | NavigationRail from medium through extra-large width (the NavigationSuiteScaffold default), expanded rail (not drawer) under M3 Expressive |
| Top bar | TopAppBar / MediumTopAppBar / LargeTopAppBar / CenterAlignedTopAppBar | Choose by headline presence; collapsing variants for rich headers |
| Cards | ElevatedCard hero, Card primary, OutlinedCard secondary | Optional team hierarchy; use theme surface/elevation defaults |
| Adaptive layouts | ListDetailPaneScaffold, SupportingPaneScaffold, NavigationSuiteScaffold | Feed grids for homogeneous content |
| Detail views | Sheet for a focused task; destination or detail pane for deep content | Preserve context and back behavior |
| Motion | Container transforms, AnimatedVisibility, animateContentSize, spring() | M3 Expressive defaults |
| Edge-to-edge | enableEdgeToEdge() + windowInsetsPadding | Mandatory, non-optional for targetSdk 36+ (windowOptOutEdgeToEdgeEnforcement removed) |
| Predictive back | Navigation and Material component defaults | Custom PredictiveBackHandler only for custom gesture-driven behavior |
| Press feedback | Standard clickable Material component or Modifier.clickable | Default indication provides feedback; custom scale motion is optional |
| Chip groups | FlowRow | Use when spatial context matters; avoid horizontal scroll for finite sets |
| Peer view switch | SegmentedButton or TabRow | Reduces navigation depth vs. separate screens |
| Touch targets | ≥ 48dp on all interactive elements | Platform minimum |
| Token discipline | No magic numbers in screen files | Tokenize spacing, elevation, shape, color |
Defaults
- Start from Material 3; select Expressive when it fits the app's design system and task density.
- Treat Compose Material 3 as the modern native baseline for navigation, surfaces, typography, color, shape, and motion.
- Enable dynamic color on Android 12+ and provide a well-tuned static seed palette as fallback.
- Prefer
MaterialTheme.typography, MaterialTheme.colorScheme, MaterialTheme.shapes, and standard containers before custom styling.
- Keep navigation familiar: adaptive bar/rail for peer sections, the app's navigation host for drill-down, sheets for focused tasks.
- Use emulator screenshot-driven verification after the screenshot is tied to the current build. Preserve app data unless stale state or signing mismatch actually requires a reset.
Platform Currency
Look these up each session; never hard-code them in advice or generated code:
- Current Android release and API level: check developer.android.com/about/versions before calling any version "current" or "latest".
- Google Play target API policy: the required target API level and its deadline move roughly once a year. Check developer.android.com/google/play/requirements/target-sdk rather than hardcoding a number.
- Material 3 Expressive: check the installed artifact and release notes for each API's availability and annotation. Release channel and opt-in status are separate:
Material3ExpressiveApi does not require opt-in; ExperimentalMaterial3ExpressiveApi does.
- Compose BOM: look up the current stable BOM at developer.android.com/develop/ui/compose/bom/bom-mapping rather than pinning an exact version in generated code.
- Predictive back is default for apps targeting API 36+ on Android 16+ devices;
onBackPressed() and KEYCODE_BACK are no longer dispatched at that target. Prefer supported navigation/component handling; custom interception can disable system animations.
- Edge-to-edge is fully mandatory, not just default-on:
windowOptOutEdgeToEdgeEnforcement is deprecated and has no effect once targetSdk reaches 36. Any recommendation that relies on opting out of edge-to-edge is stale.
Runtime Proof Gate
- Do not trust screenshots until the installed package and visible screen are tied to the current build and variant.
- Start with
adb install -r so saved state and the exact repro survive. Clear data or uninstall only after capturing the current state and proving stale state, schema incompatibility, or a signing mismatch.
- If install or launch is failing, stop design iteration and fix runtime truth before continuing.
- Use ADB and Gradle from the command line when Android Studio is not available.
Core Workflow
- Define the screen's primary job and the one or two pieces of content that must win first attention.
- Choose the Material structure first:
NavigationBar, NavigationRail, Scaffold, TopAppBar, ModalBottomSheet, ListDetailPaneScaffold, or adaptive scaffold.
- Apply typography, spacing, and color using Material tokens before inventing a custom scale.
- Use
currentWindowAdaptiveInfo(supportLargeAndXLargeWidth = true) and isWidthAtLeastBreakpoint(...); verify compact, medium, expanded, and Large/Extra-large for desktop or connected-display targets. Load material-layout-spacing.md for ordered checks, height constraints, and hinge handling.
- Verify with emulator: build, preserve repro state, replace-install, launch, capture screenshot, inspect, fix, repeat. Clear data only when state is the variable under test.
Before accepting a visual change, capture the affected screen in populated, loading, empty, error, and disabled/permission states that the feature can reach. Cross those states with the smallest device/configuration set that can expose the risk: compact + expanded width for adaptive changes, 200% font scale for text/layout changes, light + dark for color/material changes, and TalkBack focus order for semantic changes. A single polished happy-path screenshot is presentation evidence, not state coverage.
Design Craft Checklist
Before writing or reviewing screen code, check these patterns from references/design-craft-patterns.md:
- Token discipline: Every spacing, elevation, shape, color, and repeated typography value traces to a named token. No magic numbers in screen files. If a
TextStyle(fontSize = N.sp, fontWeight = W) pattern appears 3+ times, extract it to a typography token or use the Material type scale.
- Data containment: Values live in Cards,
ListItem rows, or grid cells with visual boundaries, not floating in unbounded Column space.
- Visual anchoring: Data labels include Material icons (18-20dp). Categorized lists use colored dot indicators (6dp Canvas circles) for instant visual grouping.
- Card hierarchy: Use a distinct hero treatment when the task needs it;
ElevatedCard/Card/OutlinedCard is a team default, not a platform-required hierarchy. Keep shape/elevation values in theme tokens.
- Data visualization: Use
Canvas for custom charts (radar, rings, gauges), animated score rings with Animatable, and gradient score bars. See references/android-component-patterns.md.
- Interactive polish: Apply
AnimatedVisibility for entrance reveals, animateContentSize for expanding sections, spring() for natural motion, container transforms for navigation transitions, and press feedback via Indication or InteractionSource on all tappable surfaces.
- Elevation and tonal surfaces: Use
tonalElevation for surface layering rather than drop shadows alone. Material 3 uses tonal color overlays to express elevation — Surface(tonalElevation = N.dp) shifts surface color automatically.
- Localization readiness (strings, plurals and pipeline are owned by software-localisation): All user-facing strings go through
strings.xml, including short labels. Test longer translations, Arabic RTL, and CJK locales for overflow and mirroring.
- Competitor awareness: Check competitor patterns before inventing novel solutions. Borrow containment and anchoring patterns, not brand identities.
- Adaptive layout: Verify reflow and navigation chrome against window width, height, and posture; a tablet in split-screen can have compact width.
- Accessibility: All touch targets >= 48dp. All decorative images have
contentDescription = null. All meaningful images and icons have descriptive contentDescription. Use semantics { } to merge related elements for TalkBack and provide custom actions.
- Shape consistency: Use
MaterialTheme.shapes scale (ExtraSmall through ExtraLarge) rather than ad-hoc RoundedCornerShape values. Keep shape language consistent across Cards, Buttons, Chips, and Sheets.
Design Review Loop
- Prefer Android Studio Layout Inspector for hierarchy and bounds inspection when a project is runnable.
- If Layout Inspector is unavailable, use ADB screencap + uiautomator dump as fallback for screenshots and hierarchy XML.
- Prefer side-by-side before/after screenshots over verbal "looks better" claims.
- Ask for evidence of font scaling (100%, 130%, 200%), dark/light theme, and
WindowSizeClass behavior when a change touches layout or hierarchy.
See:
Expert Judgment
Decisions a senior Android design reviewer makes that go beyond checklist compliance:
Compose vs. Views
Default to Compose for any new screen or greenfield module — it is the modern native baseline and this skill assumes it. Views still legitimately win in narrower cases:
- Large existing View/XML codebases where a full rewrite is not funded — interop via
ComposeView/AndroidView at the screen boundary, migrate incrementally, do not force a big-bang rewrite for a design fix.
- Performance-critical custom rendering with
SurfaceView/TextureView (camera preview, video, some game-like canvases) — Compose's AndroidView interop works but adds indirection; a thin View layer can still be simpler when the surface needs precise frame-timing control.
- Heavy
RecyclerView with complex diffing/animations already tuned — LazyColumn is capable, but do not force a rewrite of a well-optimized, stable RecyclerView adapter purely for "modernization" without a design or velocity reason.
- Flag it as a smell, not a rule, when a team defaults to Views for a brand-new screen — ask why, since Compose Material 3 is the better-supported path for adaptive layouts, dynamic color, and Material 3 Expressive.
Design-System Governance
- A token system only pays off if it is enforced, not just documented. If screen files still contain 3+ repeated raw
TextStyle/Color(0xFF...)/dp literals, the design system has a governance gap, not a discipline gap — recommend a lint rule (e.g., Compose lint check or custom detekt rule) over a style-guide reminder.
- When a design system diverges from Material 3 defaults (custom shape scale, custom type ramp), require the divergence to be named and centralized in one theme file — never let ad-hoc per-screen overrides recreate "shadow tokens."
- Treat Material 3 Expressive adoption as a system-wide decision, not a per-screen one: mixing baseline M3 shapes/motion with Expressive shapes/motion in the same app reads as visually inconsistent. Pick one posture per app (or per clearly-scoped surface) and document why.
- Keep baseline M3 for dense productivity surfaces when larger shapes or prominent motion obscure comparison or reduce usable space. Retain an established brand system when migration provides no user benefit; reduce decorative motion for motion-sensitive users. This is a design judgment, not a Google prohibition on Expressive.
Navigation and Predictive Back
- For new Compose navigation that needs multi-pane scenes, consider Navigation 3
NavDisplay; preserve a working Navigation Compose NavHost for a scoped visual fix. Navigation 3 uses scene strategies to render one or more entries, so test pane changes during window resizing. Load android-component-patterns.md when choosing scene transitions.
- Design back as a preview of the actual destination: test completion and cancellation from both swipe edges, including sheet dismissal and search exit. Prefer navigation/Material handling; use custom progress-driven handling only when the default cannot express the interaction, and preserve state when the gesture is cancelled.
Platform Convention vs. Brand Identity
- Keep interaction models (navigation placement, back behavior, sheet vs. dialog choice, gesture conventions) aligned with platform convention even when a brand wants to differentiate — users' muscle memory for Android navigation is a usability asset, not a constraint to design around.
- Spend brand differentiation budget on what users actually perceive as "your app": color, shape language, motion character, illustration, voice/tone in copy. These can diverge from stock Material without confusing users.
- When a brand team pushes a fully custom navigation paradigm (e.g., no back button, custom bottom bar behavior) purely for differentiation, push back with the platform-convention cost: broken predictive-back expectations, broken TalkBack navigation order, and accessibility-services friction, all for a gain that is rarely measurable in retention.
Foldable and Large-Screen Failure Modes
Common mistakes that pass phone-only review but fail on foldables/tablets:
- Fixed single-pane layout stretched to expanded width — constrain reading width or switch panes at
isWidthAtLeastBreakpoint(WindowSizeClass.WIDTH_DP_EXPANDED_LOWER_BOUND); make a separate Large/Extra-large choice when extra space supports another pane.
- Not handling the fold seam / hinge — content or interactive elements landing exactly on the hinge on a foldable's tabletop or book posture. Use
WindowInfoTracker/FoldingFeature to detect the hinge and avoid placing critical controls there.
- Navigation chrome selected by device label —
NavigationSuiteScaffold uses a bar for compact width, compact height, or tabletop posture; otherwise it uses a rail. A tablet can still need the bar in a constrained window.
- Orientation/resizability assumptions baked into layout code — assuming portrait-only or a fixed activity size breaks multi-window and free-form resizing. At targetSdk 36 (Android 16), on displays with the smallest width ≥600dp, the system ignores
screenOrientation, resizableActivity, min/maxAspectRatio, and setRequestedOrientation() (games are exempt via appCategory); a temporary opt-out (PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY) stops working at targetSdk 37. Verify behavior with the emulator's resizable/foldable device profiles, not just a fixed Pixel phone skin, and check Play's adaptive app quality tiers — verify current thresholds at developer.android.com/docs/quality-guidelines/adaptive-app-quality.
- Testing only at compact and expanded, skipping medium — the foldable inner-display and small-tablet band (600-839dp) is where two-column layouts most often look cramped or where a rail/nav choice is wrong; do not skip it as "close enough" to either neighbor.
Route Elsewhere
Navigation
References
- references/design-craft-patterns.md — token discipline, data containment, visual anchoring, dark theme, competitor analysis, and common anti-patterns
- references/material-layout-spacing.md — 8dp grid, edge-to-edge, spacing discipline, adaptive layouts, and dashboard density
- references/material-typography-color.md — Material type scale, dynamic color, tonal palettes, custom fonts, contrast, and accessibility
- references/android-component-patterns.md — NavigationBar/Rail/Drawer, TopAppBar, Scaffold, Cards, Chips, Canvas visualizations, and interactive animations
- references/android-dashboard-design.md — overview-screen hierarchy, data display patterns, card hierarchy, and content-heavy dashboard heuristics
- references/ai-design-review-android.md — screenshot prompts, review checklists (structure, token audit, containment, anchoring), and proof expectations
- references/android-studio-design-loop.md — current Android Studio and emulator verification flow
- data/sources.json — primary sources and freshness-check targets
Scripts
These are optional helpers for a proven-build design loop. If the app cannot be built, installed, or launched cleanly, fix the build before staying in this skill.
Anti-Patterns
- Do not invent custom chrome before checking whether standard Material 3 structure already solves the hierarchy problem.
- Do not hardcode
Color(0xFF...) values in Composables — use MaterialTheme.colorScheme roles so light, dark, and dynamic color adapt automatically.
- Do not use fixed
sp sizes outside the Material type scale without justification — prefer MaterialTheme.typography roles.
- Do not rely on elevation shadows alone for depth — Material 3 uses
tonalElevation (tonal color shift) as the primary depth signal; shadows are supplementary.
- Do not wrap
Box(Modifier.clickable { }) without providing a ripple Indication — it produces a dead-feeling tap with no visual response.
- Do not use
ModalBottomSheet as the sole navigation mechanism — it is for secondary detail, not primary app structure.
- Do not skip
WindowInsets consumption after calling enableEdgeToEdge() — content will render behind system bars.
- Do not place an unbounded-height
LazyColumn inside a vertical-scrolling parent; it throws during measurement. Prefer one mixed-item LazyColumn. A bounded-height child or different-axis scroller is valid; see Google's nesting examples.
- Test fixed card heights at 200% font scale; prefer content-driven height. A minimum height can still grow with content.
- Do not use
pointerInput with detectTapGestures for simple click handling — use Modifier.clickable which provides accessibility semantics, ripple, and focus handling for free.
- Do not treat dashboard heuristics such as card count or density as Google rules.
- Do not hardcode
Color.White or Color.Black for text or surfaces — use MaterialTheme.colorScheme.onSurface, onSurfaceVariant, or outline roles.
- Do not skip
contentDescription on meaningful icons and images — TalkBack users get no information from undescribed elements.
- Do not use
fillMaxWidth() on every element without considering medium and expanded WindowSizeClass — content stretches to unreadable line lengths on tablets.
- Do not dismiss
ModalBottomSheet and present a new one in the same frame — set state to hidden first, then present the new sheet after recomposition.
- Do not put temporal controls (sliders, scrubbers) inside bottom sheets — they belong in the main layout where they stay visible during visualization interaction.
- Do not start Canvas
animatableProgress at 0f for data-driven views — data may load after composition, leaving the Canvas empty. Start at 1f or trigger animation on data change.
- Do not use
LazyVerticalGrid with GridCells.Fixed(3) on compact phones for content that needs readable text — two columns or a single column with weight-based rows works better. Reserve Fixed(3) for icon-sized cells or compact stat grids.
- Do not use
Modifier.background() with a rounded shape without also applying .clip() to the same shape — child content will bleed past the rounded corners. Use Surface(shape = ...) instead, which clips automatically.
- Do not use
rememberSaveable for large data objects or lists — it serializes to Bundle which has a strict 1MB transaction limit. Use a ViewModel for screen-level data state; use rememberSaveable only for primitive UI state (selected tab index, scroll position, sheet expanded boolean).
- For Strong Skipping and visualization state/recomposition, load android-native's state guidance.
Verification Gate
Before concluding a design recommendation or audit, confirm all of the following:
Use Platform Currency and data/sources.json for platform/dependency decisions; consult the installed artifact's API reference before changing components.
Learnings Loop
When prior decisions or pitfalls are relevant, consult learnings.consolidated.md if present; use learnings.md only for needed history or as the available fallback. Otherwise skip both.
After applying it, if you encountered a pattern worth remembering, a mistake worth preventing, or a domain fact that surprised you, append one dated bullet to learnings.md via agents-skills-feedback-loop/scripts/append_learning.py. Do not modify SKILL.md itself.