Atlas / Skills / chrisbanes / Compose Focus Navigation

Compose Focus NavigationSAFE

skills/chrisbanes/compose-focus-navigation

Skills for Kotlin, Jetpack Compose, and Android development

Verdict
SAFE
Grade
B
Trust score
89 /100
Version
—
Hosts
—
License
Apache-2.0
Stars
1,096
01

Overview

Skills for Kotlin, Jetpack Compose, and Android development

Read from source at commit a5530e1d2468OBSERVED · 2026-10-12
02

What it tells the agent

The instruction file, verbatim from the audited commit — this is the text the model reads, and the surface the audit's instruction layer examines. Quoted here so you can judge it without cloning anything.

---
name: compose-focus-navigation
description: Use when writing or reviewing Jetpack Compose UI for TV, keyboard, desktop, accessibility focus, D-pad navigation, FocusRequester, focusProperties, key events, or initial focus behavior.
---

# Compose: focus navigation

## Core principle

Focus is stateful UI behavior: make targets and exceptional edges explicit, then drive and verify it through the user's keyboard, D-pad, or remote input.

## Procedure

1. Start with components that already participate in focus. Add a hook only for a requested behavior:

| Need | Add |
|---|---|
| Normal button/text field/clickable focus | Nothing extra; use the focusable component |
| Programmatic initial/restored focus | `FocusRequester` + `Modifier.focusRequester(...)` |
| Visual or state reaction to focus changes | `Modifier.onFocusChanged { ... }` |
| Custom interactive surface that is not already focusable | `Modifier.focusable()` plus role/semantics as appropriate |

2. Request initial or restored focus from `LaunchedEffect`, keyed to the condition that makes the target present. For lazy content, keep requesters by stable item id and request only after the item is composed. Inside `AnimatedContent`, use the content lambda's target consistently for rendered identity, tags, requester ownership, and the effect key; captured outer state gives outgoing and incoming content the same identity.
   In a review, a finding about `requestFocus()` running in the composable body
   is incomplete until it names the event or keyed effect that should issue
   the request after its target is available.
3. Keep default spatial search unless a concrete edge, jump, or trap is wrong. Encode only those exceptions with `focusProperties`.
4. Handle keys only for behavior that is not normal click or traversal. Consume exactly the handled event; throttle rapid D-pad work at its expensive owner, not across the screen.
5. Restore by semantic identity after refresh: retain the focused id when it exists, otherwise choose a deterministic fallback.
6. Test with one concrete key/D-pad interaction and focused semantics. When the behavior under review is user-triggered, do not substitute direct state mutation or leave the input conditional. Use screenshots only for the focus appearance.
7. Finish when all intentional targets and exceptional edges are encoded, loading/refresh behavior has a stable focus policy, and tests use the same input model as users.

For example, request and observe focus only when both behaviors are required:

```kotlin
val requester = remember { FocusRequester() }

Button(
    onClick = onClick,
    modifier = Modifier
        .focusRequester(requester)
        .onFocusChanged { state -> isFocused = state.isFocused },
) {
    Text("Play")
}
```

Call focus requests from an effect, not the composable body:

```kotlin
val initialFocus = remember { FocusRequester() }

LaunchedEffect(initialFocus) {
    initialFocus.requestFocus()
}
```

If the target appears after loading, key the request to the condition:

```kotlin
LaunchedEffect(items.isNotEmpty()) {
    if (items.isNotEmpty()) {
        firstItemRequester.requestFocus()
    }
}
```

Use `focusProperties` only when default spatial search is wrong:

```kotlin
Modifier.focusProperties {
    up = headerRequester
    down = firstRowRequester
    left = FocusRequester.Cancel
}
```

Too many hard-coded links create stale focus graphs. For special key behavior, consume only the handled event:

```kotlin
Modifier.onPreviewKeyEvent { event ->
    if (event.type == KeyEventType.KeyUp && event.key == Key.Back) {
        onBack()
        true
    } else {
        false
    }
}
```

Test focus through user input:

```kotlin
composeTestRule.onNodeWithTag("screen").performKeyInput {
    pressKey(Key.DirectionDown)
}

composeTestRule.onNodeWithTag("play-button").assertIsFocused()
```

Broader test-shape choices are in [Compose UI testing patterns](../compose-ui-testing-patterns/SKILL.md).
03

Trust audit

SAFEgrade B · trust 89/100 Nothing in the source contradicts what it says it does. Grade A is reserved for packages that have also passed the behavioural sandbox.

LayerWhat it checksResult
L0Provenance & inventoryPASS
L1Static analysis of the codeNA
L2Instruction surface (what it tells the agent)PASS
L3Class-specific surfacePASS
L4Behavioural (sandbox)SKIPPED

What the source does

Filesystem
none-observed
Network
none-observed
Shell
none-observed
Dependencies
pinned
Secrets in source
none-found

Findings (1)

LOWInventory / provenance · inv.symlink · CWE-1104
CLAUDE.md
CLAUDE.md
Why it matters. link not followed

Gates applied: no_behavioural_pass.

Audited 2026-10-12 · audit v0.4.1 · source sha a5530e1d2468full audit observations/trust-audit/skill/chrisbanes__compose-focus-navigation.json · Report an issue / request a re-scan
04

Audit history

Every audit this skill has had.

DateSourceVerdictGradeScoreChange
2026-10-12a5530e1d2468SAFEB89first audit
05

Questions

What does the Compose Focus Navigation skill do?

Skills for Kotlin, Jetpack Compose, and Android development

Is Compose Focus Navigation safe to install?

The audit found nothing in the source that contradicts what it says it does, and graded it B (89/100). Grade A is held back for packages that have also passed a sandboxed behavioural run, which is why a clean skill reads B.

What can Compose Focus Navigation access on my machine?

The audit observed no filesystem, network or shell use at all in its source.

How current is this page?

The grade is for one exact copy of the source (a5530e1d2468), read on 2026-10-12. The repository is watched, and a new audit runs when it changes — this is the first audit.

Advertisement