@lattice-ui/focus Part of the stable direction toward v1.0.
What It Is For
Use FocusScope when a section of the UI needs to trap GuiService.SelectedObject and optionally restore focus when that scope deactivates.
It is a foundation package for overlays and composite interaction regions rather than a styled control on its own.
TSX Example Preview
Preview the first lines below, or expand to inspect the full source file.
import { FocusScope } from "@lattice-ui/focus";
import React from "@rbxts/react";
export function FocusExample() {
const [active, setActive] = React.useState(true);
... import { FocusScope } from "@lattice-ui/focus";
import React from "@rbxts/react";
export function FocusExample() {
const [active, setActive] = React.useState(true);
return (
<frame BackgroundTransparency={1} Size={UDim2.fromOffset(420, 180)}>
<FocusScope active={active} restoreFocus={true} trapped={true}>
<frame
BackgroundColor3={Color3.fromRGB(34, 41, 54)}
BorderSizePixel={0}
Size={UDim2.fromOffset(320, 92)}
>
<uicorner CornerRadius={new UDim(0, 10)} />
<textlabel
BackgroundTransparency={1}
Position={UDim2.fromOffset(12, 12)}
Size={UDim2.fromOffset(290, 44)}
Text="Active FocusScope keeps selection inside this region until the scope is released."
TextColor3={Color3.fromRGB(236, 240, 248)}
TextSize={14}
TextWrapped={true}
TextXAlignment={Enum.TextXAlignment.Left}
TextYAlignment={Enum.TextYAlignment.Top}
/>
</frame>
</FocusScope>
<textbutton
AutoButtonColor={false}
BackgroundColor3={Color3.fromRGB(60, 76, 104)}
BorderSizePixel={0}
Position={UDim2.fromOffset(0, 110)}
Size={UDim2.fromOffset(200, 34)}
Text={active ? "Release focus scope" : "Activate focus scope"}
TextColor3={Color3.fromRGB(236, 240, 248)}
TextSize={14}
Event={{ Activated: () => setActive((value) => !value) }}
>
<uicorner CornerRadius={new UDim(0, 8)} />
</textbutton>
</frame>
);
} Install
Global CLI command: lattice add focus
Monorepo local script
Use your package manager wrapper when running the local lattice command.
pnpm lattice add focusPublic Exports
-
FocusScope
State Model
FocusScopetracks whether it isactive, whether focus should betrapped, and whether prior focus should be restored on teardown.- Nested trapped scopes are stack-based, so only the top-most active scope redirects outside selections.
- The scope model is built around Roblox selection focus, not browser-like DOM focus graphs.
Key API
FocusScope
Use active, trapped, restoreFocus, and asChild to determine how the scope wraps and controls its descendants.
Composition Patterns
Modal or menu internals
Wrap overlay content in FocusScope when leaving the selection region would create invalid or confusing interaction.
Temporary keyboard islands
Use an active trapped scope for short-lived panels that should return the user to the previous selection when they close.
Cautions / Limits
- Current focus management is based on
GuiService.SelectedObject; it does not rewriteNextSelection*relationships for you. - Use it for focus ownership, not as a general-purpose navigation system.
Decision Guides
- Overlay and state pitfalls
Review focus ownership, trapped scopes, and restoreFocus expectations.