Component reference
FocusScope
An inline confirmation with deliberate focus handling.
Preview
Live previewOptional six-digit hex. Clear to restore the preset. Check contrast with your brand color.
Changes apply to this preview only.
Inline confirmation
A non-modal confirmation that still owns the keyboard while it is open.
Skip link target
Pair a focus scope with a skip link to move keyboard users into the main content.
Drawer body
The same scope can live inside a drawer without importing the drawer.
Width adapts to the available space
"use client";
import {
Card,
CardHeader,
CardTitle,
CardDescription,
CardContent,
Stack,
HStack,
Badge,
Text,
Box,
Button,
FocusScope
} from "@pimolu/react";
export function DemoExample() {
return (
<div
className="grid grid-cols-[repeat(auto-fit,minmax(min(100%,280px),1fr))] gap-6 min-w-0 items-start"
>
<Card>
<CardHeader>
<CardTitle>Inline confirmation</CardTitle>
<CardDescription>
A non-modal confirmation that still owns the keyboard while it
is open.
</CardDescription>
</CardHeader>
<CardContent>
<Stack gap={3}>
<HStack gap={2} align="center">
<Badge variant="warning">Draft</Badge>
<Text size="sm" tone="muted">Unsaved changes in the editor.</Text>
</HStack>
<Box
padding={4}
background="muted"
style={{
borderRadius: "var(--pimolu-radius-md)",
}}
>
<Stack gap={3}>
<Text size="sm">Publish the draft and notify the team?</Text>
<HStack gap={2} wrap>
<Button size="sm" appearance="outline">Keep editing</Button>
<Button size="sm">Publish</Button>
</HStack>
</Stack>
</Box>
<Text size="sm" tone="muted">
The confirmation can wrap the actions in a FocusScope and set
active while it is open; the page behind it stays interactive.
</Text>
</Stack>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Skip link target</CardTitle>
<CardDescription>
Pair a focus scope with a skip link to move keyboard users into
the main content.
</CardDescription>
</CardHeader>
<CardContent>
<Stack gap={3}>
<Text size="sm" tone="muted">
A skip link focuses the main region; the scope then keeps Tab
inside until the user leaves it deliberately.
</Text>
<FocusScope as="section" aria-label="Main content demo" active={false}>
<Box
padding={4}
background="surface"
style={{
borderRadius: "var(--pimolu-radius-md)",
}}
>
<Stack gap={2}>
<Text weight="medium">Main content</Text>
<Text size="sm" tone="muted">
An inactive scope renders no trap and leaves the normal page
order intact.
</Text>
</Stack>
</Box>
</FocusScope>
</Stack>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Drawer body</CardTitle>
<CardDescription>
The same scope can live inside a drawer without importing the
drawer.
</CardDescription>
</CardHeader>
<CardContent>
<Stack gap={3}>
<HStack gap={2} align="center">
<Badge variant="secondary">Settings</Badge>
<Text size="sm" tone="muted">Focus returns to the menu button on close.</Text>
</HStack>
<Stack gap={2}>
<Button size="sm" appearance="outline" fullWidth>Account</Button>
<Button size="sm" appearance="outline" fullWidth>Appearance</Button>
<Button size="sm" appearance="outline" fullWidth>Notifications</Button>
</Stack>
</Stack>
</CardContent>
</Card>
</div>
);
}More examples
Different configurations and compositions using the real Pimolu API. Each preview runs independently and follows the site appearance.
Playground
Activate the scope, then use Tab and Shift+Tab. Deactivate it to see the restore behavior.
Container focus
Explore container focus with FocusScope.
Skip link target
A skip link target composition using FocusScope.
Usage
Use a scope only when the interaction calls for managed focus; avoid trapping ordinary page content.
API reference
Generated from the public TypeScript declarations. Native HTML attributes are inherited where the component extends them; this reference includes Pimolu props and selected common native attributes.
PimoluFocusScopeProps
Type source| Name | Type | Default | Description |
|---|---|---|---|
as | ElementType | undefined | `div` | Element rendered by the scope. Defaults to `div`. |
active | boolean | undefined | `true` | Enables the scope: focus moves inside on activation, and is restored on deactivation (see `restoreFocus`). When false, nothing happens. Defaults to `true`. |
trapped | boolean | undefined | `true` | Keep Tab and Shift+Tab inside the scope. Set it to false for a scope that only owns the initial focus. Defaults to `true`. |
restoreFocus | boolean | undefined | `true` | Return focus to the element focused before activation. When false, focus stays wherever it is when the scope deactivates. Defaults to `true`. |
initialFocus | PimoluFocusScopeInitialFocus | undefined | `first` | Focus the first focusable child, or the container itself (useful when the scope holds no interactive control yet). Defaults to `first`. |
defaultValue | string | number | readonly string[] | undefined | — | Standard React/HTML attribute. |
className | string | undefined | — | Standard React/HTML attribute. |
style | CSSProperties | undefined | — | Standard React/HTML attribute. |
aria-label | string | undefined | — | Defines a string value that labels the current element. |
children | ReactNode | — | Standard React/HTML attribute. |
onChange | ChangeEventHandler<HTMLElement, Element> | undefined | — | Standard React/HTML attribute. |
onClick | MouseEventHandler<HTMLElement> | undefined | — | Standard React/HTML attribute. |
asElementType | undefinedDefault: `div`
Element rendered by the scope. Defaults to `div`.
activeboolean | undefinedDefault: `true`
Enables the scope: focus moves inside on activation, and is restored on deactivation (see `restoreFocus`). When false, nothing happens. Defaults to `true`.
trappedboolean | undefinedDefault: `true`
Keep Tab and Shift+Tab inside the scope. Set it to false for a scope that only owns the initial focus. Defaults to `true`.
restoreFocusboolean | undefinedDefault: `true`
Return focus to the element focused before activation. When false, focus stays wherever it is when the scope deactivates. Defaults to `true`.
initialFocusPimoluFocusScopeInitialFocus | undefinedDefault: `first`
Focus the first focusable child, or the container itself (useful when the scope holds no interactive control yet). Defaults to `first`.
defaultValuestring | number | readonly string[] | undefinedNo default extracted.
Standard React/HTML attribute.
classNamestring | undefinedNo default extracted.
Standard React/HTML attribute.
styleCSSProperties | undefinedNo default extracted.
Standard React/HTML attribute.
aria-labelstring | undefinedNo default extracted.
Defines a string value that labels the current element.
childrenReactNodeNo default extracted.
Standard React/HTML attribute.
onChangeChangeEventHandler<HTMLElement, Element> | undefinedNo default extracted.
Standard React/HTML attribute.
onClickMouseEventHandler<HTMLElement> | undefinedNo default extracted.
Standard React/HTML attribute.
Accessibility
- Check the initial focus target, Tab boundaries, Escape handling and restoration.