focus-engine
Implements keyboard, directional, and scene-level focus behavior across SwiftUI and UIKit. Use when managing @FocusState, defaultFocus, focused values, focusable interactions, focus sections, tvOS geometric focus and Siri Remote navigation, watchOS Digital Crown input, visionOS connected-device focus versus gaze hover/input targets, macOS key view loop and Full Keyboard Access, focus restoration after presentation changes, custom focus routing with UIFocusGuide, or debugging focus with UIFocusDebugger.
What this skill does
# Focus Engine
Focus behavior for SwiftUI and UIKit apps targeting iOS 26+, iPadOS, macOS, tvOS, and visionOS connected-input paths. Covers keyboard focus, directional focus, scene-focused values, focus restoration, and UIKit focus guides. `focusSection()` guidance in this skill applies to macOS and tvOS. visionOS gaze-driven hover is an input affordance, not focus. Accessibility-specific focus for VoiceOver and Switch Control lives in the `ios-accessibility` skill.
When a request mixes focus with accessibility or spatial input, keep the boundary explicit:
- Use this skill for keyboard, remote, game-controller, and scene focus behavior.
- For visionOS, describe gaze, direct touch, and pointer targeting as hover/input affordances, not focus.
- For VoiceOver, Switch Control, Voice Control, or accessibility element ordering, give only a brief handoff to `ios-accessibility`.
## Contents
- [SwiftUI FocusState](#swiftui-focusstate)
- [Default Focus](#default-focus)
- [Focused Values and Scene Values](#focused-values-and-scene-values)
- [Focusable Interactions](#focusable-interactions)
- [Focus Sections](#focus-sections)
- [Focus Restoration](#focus-restoration)
- [UIKit Focus Guides](#uikit-focus-guides)
- [Common Mistakes](#common-mistakes)
- [Review Checklist](#review-checklist)
- [References](#references)
## SwiftUI FocusState
Use `@FocusState` to read and write focus placement inside a scene. Use `Bool` for a single target or an optional `Hashable` enum for multiple targets.
```swift
struct LoginView: View {
enum Field: Hashable { case email, password }
@State private var email = ""
@State private var password = ""
@FocusState private var focusedField: Field?
var body: some View {
Form {
TextField("Email", text: $email)
.focused($focusedField, equals: .email)
SecureField("Password", text: $password)
.focused($focusedField, equals: .password)
}
.onAppear { focusedField = .email }
.onSubmit {
switch focusedField {
case .email: focusedField = .password
case .password, nil: submit()
}
}
}
}
```
Keep focus state local to the view that owns the focusable controls.
## Default Focus
Use `.defaultFocus` to set the preferred initial focus region or control when a view appears or when focus is reassigned automatically.
```swift
struct SidebarView: View {
enum Target: Hashable { case library, settings }
@FocusState private var focusedTarget: Target?
var body: some View {
VStack {
Button("Library") { }
.focused($focusedTarget, equals: .library)
Button("Settings") { }
.focused($focusedTarget, equals: .settings)
}
.defaultFocus($focusedTarget, .library)
}
}
```
Prefer one clear default destination per screen or focus region.
## Focused Values and Scene Values
Use focused values to expose state from the currently focused view. Use scene-focused values when commands or scene-wide UI should keep access to the value even after focus moves within that scene.
```swift
struct SelectedRecipeKey: FocusedValueKey {
typealias Value = Binding<Recipe>
}
extension FocusedValues {
var selectedRecipe: Binding<Recipe>? {
get { self[SelectedRecipeKey.self] }
set { self[SelectedRecipeKey.self] = newValue }
}
}
struct RecipeDetailView: View {
@Binding var recipe: Recipe
var body: some View {
Text(recipe.title)
.focusedSceneValue(\.selectedRecipe, $recipe)
}
}
```
Use this pattern for menus, commands, and toolbars that need to act on the focused scene's current content.
## Focusable Interactions
Use `.focusable(_:interactions:)` on custom SwiftUI views that should participate in keyboard or directional focus.
```swift
struct SelectableCard: View {
let title: String
let action: () -> Void
@FocusState private var isFocused: Bool
var body: some View {
Button(action: action) {
RoundedRectangle(cornerRadius: 12)
.fill(isFocused ? Color.accentColor.opacity(0.15) : .clear)
.overlay { Text(title) }
}
.buttonStyle(.plain)
.focusable(interactions: .activate)
.focused($isFocused)
}
}
```
Prefer semantic `Button`, `Toggle`, `TextField`, and other system controls before making arbitrary gesture-driven views focusable. Use `.focusable(interactions: .activate)` for custom button-like controls only when a semantic control cannot express the UI. Reserve broader interactions for views that genuinely need editing or multiple focus-driven behaviors.
## Focus Sections
Use `focusSection()` on macOS 13+ and tvOS 15+ to guide directional movement across groups of focusable descendants in uneven layouts.
```swift
struct TVLibraryView: View {
var body: some View {
HStack {
VStack {
Button("Recent") { }
Button("Favorites") { }
Button("Downloaded") { }
}
.focusSection()
VStack {
Button("Featured") { }
Button("Top Picks") { }
Button("Continue Watching") { }
}
.focusSection()
}
}
}
```
Use focus sections on macOS and tvOS when default left/right or up/down movement skips the intended group.
## Focus Restoration
After dismissing a sheet, popover, or transient overlay, return focus to a stable trigger or logical next target.
```swift
struct FiltersView: View {
@State private var showSheet = false
@FocusState private var isFilterButtonFocused: Bool
var body: some View {
Button("Filters") { showSheet = true }
.focused($isFilterButtonFocused)
.sheet(isPresented: $showSheet) {
FilterEditor()
.onDisappear {
Task { @MainActor in
isFilterButtonFocused = true
}
}
}
}
}
```
Restore focus intentionally whenever presentation changes would otherwise leave users disoriented.
## UIKit Focus Guides
Use `UIFocusGuide` when UIKit or tvOS layouts need custom routing across empty space or awkward geometry.
```swift
final class DashboardViewController: UIViewController {
private let focusGuide = UIFocusGuide()
@IBOutlet private weak var leadingButton: UIButton!
@IBOutlet private weak var trailingButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
view.addLayoutGuide(focusGuide)
focusGuide.preferredFocusEnvironments = [trailingButton]
NSLayoutConstraint.activate([
focusGuide.leadingAnchor.constraint(equalTo: leadingButton.trailingAnchor),
focusGuide.trailingAnchor.constraint(equalTo: trailingButton.leadingAnchor),
focusGuide.topAnchor.constraint(equalTo: leadingButton.topAnchor),
focusGuide.bottomAnchor.constraint(equalTo: leadingButton.bottomAnchor)
])
}
}
```
`UIFocusGuide` is invisible and not a view. Use it to redirect focus without adding decorative UI.
## Common Mistakes
1. Mixing accessibility focus and keyboard or directional focus in the same mental model.
2. Storing `@FocusState` in shared models instead of the owning view.
3. Setting multiple competing default focus targets on one screen.
4. Using `.focusable()` on decorative views.
5. Forgetting focus restoration after sheets, popovers, or custom overlays.
6. Reaching for `UIFocusGuide` before trying `focusSection()` on macOS or tvOS, or better layout grouping in SwiftUI.
7. Using gesture handlers for primary actions on custom focusable controls instead of a semantic `Button` when possible.
8. Treating visionOS gaze hover as focus; reserve focus guidance for connected input such as keyboards and game controlleRelated in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.