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:
Material3ExpressiveApidoes not require opt-in;ExperimentalMaterial3ExpressiveApidoes. - 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()andKEYCODE_BACKare 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:
windowOptOutEdgeToEdgeEnforcementis 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 -rso 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)andisWidthAtLeastBreakpoint(...); 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,
ListItemrows, 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/OutlinedCardis a team default, not a platform-required hierarchy. Keep shape/elevation values in theme tokens. - Data visualization: Use
Canvasfor custom charts (radar, rings, gauges), animated score rings withAnimatable, and gradient score bars. See references/android-component-patterns.md. - Interactive polish: Apply
AnimatedVisibilityfor entrance reveals,animateContentSizefor expanding sections,spring()for natural motion, container transforms for navigation transitions, and press feedback viaIndicationorInteractionSourceon all tappable surfaces. - Elevation and tonal surfaces: Use
tonalElevationfor 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 descriptivecontentDescription. Usesemantics { }to merge related elements for TalkBack and provide custom actions. - Shape consistency: Use
MaterialTheme.shapesscale (ExtraSmall through ExtraLarge) rather than ad-hocRoundedCornerShapevalues. 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
WindowSizeClassbehavior when a change touches layout or hierarchy.
See:
- references/ai-design-review-android.md
- references/android-studio-design-loop.md
- scripts/capture-screenshot.sh
- scripts/layout-inspector.sh
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/AndroidViewat 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'sAndroidViewinterop works but adds indirection; a thin View layer can still be simpler when the surface needs precise frame-timing control. - Heavy
RecyclerViewwith complex diffing/animations already tuned —LazyColumnis 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...)/dpliterals, 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 ComposeNavHostfor 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/FoldingFeatureto detect the hinge and avoid placing critical controls there. - Navigation chrome selected by device label —
NavigationSuiteScaffolduses 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, andsetRequestedOrientation()(games are exempt viaappCategory); 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
- Use ../software-mobile/SKILL.md for platform selection, cross-platform decisions, or general Android architecture and implementation strategy.
- Use ../qa-testing-android/SKILL.md for Espresso, UI Automator, Compose testing, device matrix planning, or screenshot diff testing.
- Use ../software-ui-ux-design/SKILL.md for generic product UX patterns that are not Android-specific.
- Use ../software-accessibility/SKILL.md for broader accessibility remediation beyond Android-native visual design defaults.
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.
- scripts/_android_common.sh — shared helpers for ADB, emulator, and AVD resolution
- scripts/bootstrap-emulator.sh — create and boot an AVD for design iteration
- scripts/build-android.sh — compile-only Gradle build wrapper
- scripts/run-android.sh — build, install, and launch for iterative design work (
--uninstall-firstis an escalation, not the default) - scripts/capture-screenshot.sh — save an emulator screenshot for review loops
- scripts/layout-inspector.sh — dump UI hierarchy via uiautomator for structure review
- scripts/test_run_android.py — offline launcher regressions; run with
python3, using mocked ADB/Gradle
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 — useMaterialTheme.colorSchemeroles so light, dark, and dynamic color adapt automatically. - Do not use fixed
spsizes outside the Material type scale without justification — preferMaterialTheme.typographyroles. - 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 rippleIndication— it produces a dead-feeling tap with no visual response. - Do not use
ModalBottomSheetas the sole navigation mechanism — it is for secondary detail, not primary app structure. - Do not skip
WindowInsetsconsumption after callingenableEdgeToEdge()— content will render behind system bars. - Do not place an unbounded-height
LazyColumninside a vertical-scrolling parent; it throws during measurement. Prefer one mixed-itemLazyColumn. 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
pointerInputwithdetectTapGesturesfor simple click handling — useModifier.clickablewhich 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.WhiteorColor.Blackfor text or surfaces — useMaterialTheme.colorScheme.onSurface,onSurfaceVariant, oroutlineroles. - Do not skip
contentDescriptionon meaningful icons and images — TalkBack users get no information from undescribed elements. - Do not use
fillMaxWidth()on every element without considering medium and expandedWindowSizeClass— content stretches to unreadable line lengths on tablets. - Do not dismiss
ModalBottomSheetand 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
animatableProgressat0ffor data-driven views — data may load after composition, leaving the Canvas empty. Start at1for trigger animation on data change. - Do not use
LazyVerticalGridwithGridCells.Fixed(3)on compact phones for content that needs readable text — two columns or a single column with weight-based rows works better. ReserveFixed(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. UseSurface(shape = ...)instead, which clips automatically. - Do not use
rememberSaveablefor large data objects or lists — it serializes toBundlewhich has a strict 1MB transaction limit. Use aViewModelfor screen-level data state; userememberSaveableonly 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:
- Recommendation maps to standard Material 3 structure; custom patterns justified in comments
- Screenshot/emulator state from a freshly installed and launched build (not a stale install)
- Typography uses
MaterialTheme.typographyroles, not ad-hocTextStyle(fontSize = N.sp) - Color uses
MaterialTheme.colorSchemeroles; contrast verified in both light and dark themes, with dynamic color on and off - All interactive elements have touch targets ≥ 48dp
- Adaptive layout verified on compact, medium, expanded, and Large/Extra-large when targeting desktop/connected displays
- Edge-to-edge:
enableEdgeToEdge()called and all content useswindowInsetsPaddingor equivalent — not an opt-out (windowOptOutEdgeToEdgeEnforcementhas no effect at targetSdk 36+) - Back preview reaches the intended destination, handles cancellation, and preserves sheet/search behavior using supported APIs
- Dashboard/density guidance labeled as a team heuristic, not a Google platform rule
- Expressive APIs checked against installed dependencies; required opt-ins and release channel recorded separately
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.