Section
Section
Section titled “Section”Title/description/action/content composition for screen sections.
Identity
Section titled “Identity”- Category: Layout & surfaces
- Status: stable public Registry/export-map component family
- Targets: iOS · Android · Web, subject to the compatibility contract
- Source:
packages/ui/src/components/section.tsx
Import
Section titled “Import”import { Section } from '@beemvp/beeui-ui';There is no documented deep/private source import. For source ownership from a BeeUI checkout:
pnpm beeui -- add sectionRegistry metadata: registry/registry.json.
Composition and public API
Section titled “Composition and public API”- Primary export:
Section
Exported types: SectionProps
The generated API inventory is mechanically joined to packages/ui/src/index.ts, Registry metadata, and the component reference contract. For behavior details and defaults, use the canonical component behavior catalog rather than copying TypeScript declarations into a second hand-maintained table.
State and behavior contract
Section titled “State and behavior contract”Controlled/uncontrolled props, callbacks, disabled semantics, normalization/fail-safe behavior, and mount/unmount rules are defined by the public types and the canonical behavior catalog. The executable fixtures below are the source-grounded usage examples; consumers should not infer state ownership from DOM structure or another UI library.
Provider and dependencies
Section titled “Provider and dependencies”- No additional provider is required by this family.
BeeUIProviderremains the recommended application root. - Peer/native dependencies visible to this Registry item:
react,react-native - Registry dependency closure:
box,core-cn,text,theme - Safe-area ownership remains explicit: shell surfaces touching system edges opt into
SafeArea; components do not silently invent app-shell insets. - Web consumers load the BeeUI semantic theme CSS as documented in Web onboarding.
Platform behavior
Section titled “Platform behavior”The same public family is exposed across the supported target matrix; meaningful platform differences remain governed by the compatibility contract.
- Web: live browser/keyboard behavior is verified by Web-specific checks where applicable.
- iOS / Android: package/export/native compile evidence is not described as device-runtime proof. Consult the compatibility and native-preview guides for the exact evidence class.
- Platform-specific or experimental behavior is called out in the canonical component/compatibility docs rather than hidden behind a generic parity claim.
Accessibility
Section titled “Accessibility”Use the Accessibility overview, RTL/localization, and Large text & zoom alongside this family. Roles/states, keyboard/focus behavior, announcements, Dynamic Type/Web zoom, RTL, and reduced-motion expectations remain component-specific; BeeUI does not claim universal accessibility certification from automated tests.
Styling and theming
Section titled “Styling and theming”BeeUI components consume semantic tokens and support the current typed variant/density contracts. Use Theming and Density. className is an implementation escape hatch for source-owned/application work, not a cross-engine portability guarantee.
Executable examples
Section titled “Executable examples”- Primary executable fixture:
apps/showcase/__tests__/component-contracts.test.tsx - Additional fixture:
apps/showcase/component-gallery/component-gallery.tsx - Additional fixture:
apps/showcase/component-gallery/date-picker-showcase.tsx - Additional fixture:
apps/showcase/component-gallery/date-time-picker-showcase.tsx
Open the matching Web runtime in Showcase. The Showcase link demonstrates Web behavior; use the native-preview guide for real simulator/emulator/device paths.
Live Web preview
Section titled “Live Web preview”This frame loads the real BeeUI Web Showcase on demand; it is not a second docs-only implementation. It proves browser behavior only. Use native preview for iOS/Android simulator, emulator or device paths.
Composition anatomy
Section titled “Composition anatomy”- Family root / primary export:
Section- Exported type surface:
SectionProps
- Exported type surface:
The tree above is ordinary document structure so it remains readable with keyboard and assistive technology; it is derived from the real public export family rather than a canvas-only diagram.
Verified example source
Section titled “Verified example source”The following is the exact typechecked runtime Showcase fixture selected for this live preview: apps/showcase/component-gallery/component-gallery.tsx. Runtime gallery/pattern sources are preferred over test harnesses, and the displayed source and executable source are the same file; there is no separately maintained demo snippet.
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, AlertBanner, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogTitle, AlertDialogTrigger, AppHeader, Avatar, Badge, BeeThemeScope, BottomActionBar, Box, Breadcrumb, BreadcrumbItem, Button, Card, Checkbox, Chip, ChipGroup, Collapsible, CollapsibleContent, CollapsibleTrigger, DescriptionItem, DescriptionList, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogTitle, DialogTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuTrigger, EmptyState, ErrorState, Field, FormGroup, HStack, IconButton, Input, Link, ListGroup, ListGroupHeader, ListItem, OTPInput, Pagination, PaginationItem, PasswordInput, Popover, PopoverClose, PopoverContent, PopoverDescription, PopoverTitle, PopoverTrigger, Progress, Radio, RadioGroup, SafeArea, Screen, SearchInput, Section, SegmentedControl, SegmentedControlItem, Separator, SettingsItem, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetTitle, SheetTrigger, Skeleton, Spinner, Stack, Stat, StatHelpText, StatLabel, StatValue, Stepper, StepperItem, Switch, Tabs, TabsContent, TabsList, TabsTrigger, Text, Textarea, Timeline, TimelineItem, Tooltip, TooltipContent, TooltipTrigger, useToast, VStack,} from '@beemvp/beeui-ui';import * as React from 'react';import { ScrollView, StatusBar } from 'react-native';import { Uniwind, useUniwind } from 'uniwind';import { DatePickerShowcase } from './date-picker-showcase';import { DateTimePickerShowcase } from './date-time-picker-showcase';import { SelectShowcase } from './select-showcase';import { TableShowcase } from './table-showcase';
function ThemeToggle() { const { hasAdaptiveThemes, theme } = useUniwind(); const activeTheme = hasAdaptiveThemes ? 'system' : theme; const nextTheme = activeTheme === 'system' ? 'light' : activeTheme === 'light' ? 'dark' : 'system';
return ( <Button accessibilityLabel={`Theme ${activeTheme}. Switch to ${nextTheme}`} onPress={() => Uniwind.setTheme(nextTheme)} size="sm" testID="component-gallery-theme-toggle" variant="outline" > {`Theme: ${activeTheme}`} </Button> );}
function PlaygroundHeading({ children, description }: { children: string; description: string }) { return ( <VStack gap="xs"> <Text variant="heading">{children}</Text> <Text tone="muted">{description}</Text> </VStack> );}
function PlacementPopover({ placement }: { placement: 'top' | 'right' | 'bottom' | 'left' }) { return ( <Popover> <PopoverTrigger size="sm" variant="outline"> {placement} </PopoverTrigger> <PopoverContent placement={placement} testID={`popover-${placement}-content`}> <PopoverTitle>{`${placement[0].toUpperCase()}${placement.slice(1)} placement`}</PopoverTitle> <PopoverDescription> This surface is positioned by the shared anchored-overlay geometry kernel. </PopoverDescription> {placement === 'bottom' ? ( <Field label="Note"> <Input accessibilityLabel="Popover note" testID="popover-demo-input" /> </Field> ) : null} <PopoverClose size="sm" variant="ghost"> Close </PopoverClose> </PopoverContent> </Popover> );}
// Consumer React context declared below BeeUIProvider. Overlay content must// resolve the provided value, not the default — the web regression asserts this.const OverlayConsumerContext = React.createContext('overlay-context-default');
function OverlayContextValue({ testID }: { testID: string }) { return <Text testID={testID}>{`context: ${React.useContext(OverlayConsumerContext)}`}</Text>;}
// CASE C — scope-ordering proof for real-browser Escape. A single control opens,// with independent open states, a root-scope Popover AND a Dialog whose nested// DropdownMenu is also open. They live in different overlay scopes, so their// registration order is irrelevant by construction — Escape routes to the active// modal scope (the menu), never the root Popover behind it. (Strict "root opened// after menu" ordering with real timing is covered deterministically in the jest// CASE B/D contract tests; a click inside the modal cannot be used here because// the open menu's dismiss layer intercepts pointer events.)function CaseCScopeOrdering() { const [dialogOpen, setDialogOpen] = React.useState(false); const [menuOpen, setMenuOpen] = React.useState(false); const [rootOpen, setRootOpen] = React.useState(false); return ( <> <Button onPress={() => { setDialogOpen(true); setMenuOpen(true); setRootOpen(true); }} testID="overlay-context-casec-open" variant="outline" > Open CASE C </Button>
<Popover onOpenChange={setRootOpen} open={rootOpen}> <PopoverTrigger testID="overlay-context-casec-root-trigger" variant="outline"> CASE C root </PopoverTrigger> <PopoverContent placement="bottom"> <OverlayContextValue testID="overlay-context-casec-root-value" /> </PopoverContent> </Popover>
<Dialog onOpenChange={setDialogOpen} open={dialogOpen}> <DialogTrigger testID="overlay-context-casec-dialog-trigger">CASE C dialog</DialogTrigger> <DialogContent> <DialogTitle>CASE C dialog</DialogTitle> <DropdownMenu onOpenChange={setMenuOpen} open={menuOpen}> <DropdownMenuTrigger testID="overlay-context-casec-menu-trigger" variant="outline"> CASE C menu </DropdownMenuTrigger> <DropdownMenuContent> <DropdownMenuLabel>CASE C menu</DropdownMenuLabel> <OverlayContextValue testID="overlay-context-casec-menu-value" /> </DropdownMenuContent> </DropdownMenu> </DialogContent> </Dialog> </> );}
function ConsumerContextOverlays() { const [dialogMenuAction, setDialogMenuAction] = React.useState('none'); return ( <OverlayConsumerContext.Provider value="preserved"> <VStack gap="sm"> <Popover> <PopoverTrigger testID="overlay-context-popover-trigger" variant="outline"> Popover context </PopoverTrigger> <PopoverContent placement="bottom"> <PopoverTitle>Popover consumer context</PopoverTitle> <OverlayContextValue testID="overlay-context-popover-value" /> </PopoverContent> </Popover>
<DropdownMenu> <DropdownMenuTrigger testID="overlay-context-menu-trigger" variant="outline"> Menu context </DropdownMenuTrigger> <DropdownMenuContent> <DropdownMenuLabel>Menu consumer context</DropdownMenuLabel> <OverlayContextValue testID="overlay-context-menu-value" /> </DropdownMenuContent> </DropdownMenu>
<Tooltip> <TooltipTrigger testID="overlay-context-tooltip-trigger" variant="outline"> Tooltip context </TooltipTrigger> <TooltipContent> <OverlayContextValue testID="overlay-context-tooltip-value" /> </TooltipContent> </Tooltip>
<Dialog> <DialogTrigger testID="overlay-context-dialog-trigger">Dialog context</DialogTrigger> <DialogContent> <DialogTitle>Dialog with a nested overlay</DialogTitle> <Popover> <PopoverTrigger testID="overlay-context-dialog-popover-trigger" variant="outline"> Popover in dialog </PopoverTrigger> <PopoverContent placement="bottom"> <OverlayContextValue testID="overlay-context-dialog-popover-value" /> </PopoverContent> </Popover>
<Tooltip> <TooltipTrigger testID="overlay-context-dialog-tooltip-trigger" variant="outline"> Tooltip in dialog </TooltipTrigger> <TooltipContent> <OverlayContextValue testID="overlay-context-dialog-tooltip-value" /> </TooltipContent> </Tooltip>
<DropdownMenu> <DropdownMenuTrigger testID="overlay-context-dialog-menu-trigger" variant="outline"> Menu in dialog </DropdownMenuTrigger> <DropdownMenuContent> <DropdownMenuLabel>Menu in dialog</DropdownMenuLabel> <OverlayContextValue testID="overlay-context-dialog-menu-value" /> <DropdownMenuItem onSelect={() => setDialogMenuAction('selected')} testID="overlay-context-dialog-menu-item" > Select in dialog </DropdownMenuItem> </DropdownMenuContent> </DropdownMenu> <Text testID="overlay-context-dialog-menu-action">{`menu action: ${dialogMenuAction}`}</Text> </DialogContent> </Dialog>
<CaseCScopeOrdering /> </VStack> </OverlayConsumerContext.Provider> );}
// #68 — real-browser proof that a BeeThemeScope declared around an overlay// trigger is still resolved for that overlay's portaled content on web (the// `ReactDOM.createPortal` transport). The Playwright regression// (apps/visual-regression/tests/overlay-context.spec.ts) asserts these testIDs// against the real Uniwind runtime, the same way issue #35 proves consumer// context survives the same transport.function ThemeScopeValue({ testID }: { testID: string }) { const { theme } = useUniwind(); return <Text testID={testID}>{`theme: ${theme}`}</Text>;}
function ThemeScopeOverlays() { return ( <VStack gap="sm"> <BeeThemeScope appearance="dark" brand="violet"> <VStack gap="sm"> <ThemeScopeValue testID="theme-scope-root-value" />
<Popover> <PopoverTrigger testID="theme-scope-popover-trigger" variant="outline"> Popover in scope </PopoverTrigger> <PopoverContent placement="bottom"> <PopoverTitle>Popover in a BeeThemeScope</PopoverTitle> <ThemeScopeValue testID="theme-scope-popover-value" /> </PopoverContent> </Popover>
<DropdownMenu> <DropdownMenuTrigger testID="theme-scope-menu-trigger" variant="outline"> Menu in scope </DropdownMenuTrigger> <DropdownMenuContent> <DropdownMenuLabel>Menu in a BeeThemeScope</DropdownMenuLabel> <ThemeScopeValue testID="theme-scope-menu-value" /> </DropdownMenuContent> </DropdownMenu>
<Dialog> <DialogTrigger testID="theme-scope-dialog-trigger">Dialog in scope</DialogTrigger> <DialogContent> <DialogTitle>Dialog inside a BeeThemeScope</DialogTitle> <ThemeScopeValue testID="theme-scope-dialog-value" /> <DialogClose size="sm" variant="ghost"> Close </DialogClose> </DialogContent> </Dialog>
<BeeThemeScope appearance="light" brand="bee"> <ThemeScopeValue testID="theme-scope-nested-value" /> </BeeThemeScope> </VStack> </BeeThemeScope>
<ThemeScopeValue testID="theme-scope-sibling-value" /> </VStack> );}
function ToastPlayground() { const toast = useToast(); const [lastAction, setLastAction] = React.useState('No Toast action yet');
return ( <Card className="gap-4" variant="raised"> <Section description="Provider-scoped transient notifications. Up to three are visible; overflow waits FIFO. Close visible items to inspect queue promotion." title="Toast notifications" > <HStack gap="sm" wrap> <Button onPress={() => toast.show({ title: 'Saved', description: 'Default toast with a 5 second timeout.' })} size="sm" variant="outline" > Default </Button> <Button onPress={() => toast.show({ title: 'Published', description: 'Changes are live.', variant: 'success' })} size="sm" variant="outline" > Success </Button> <Button onPress={() => toast.show({ title: 'Check settings', description: 'One option needs attention.', variant: 'warning' })} size="sm" variant="outline" > Warning </Button> <Button onPress={() => toast.show({ title: 'Save failed', description: 'Try the operation again.', variant: 'destructive' })} size="sm" variant="outline" > Error </Button> <Button onPress={() => toast.show({ title: 'Item archived', description: 'You can undo this action.', variant: 'info', action: { label: 'Undo', onPress: () => setLastAction('Undo pressed') }, })} size="sm" variant="outline" > Action </Button> <Button onPress={() => toast.show({ title: 'Persistent notice', description: 'Dismiss this explicitly.', duration: 'persistent', variant: 'info' })} size="sm" variant="outline" > Persistent </Button> <Button onPress={() => { for (let index = 1; index <= 6; index += 1) { toast.show({ title: `Queued toast ${index}`, description: index <= 3 ? 'Initially visible.' : 'Promotes FIFO after a visible toast closes.', duration: 'persistent', variant: index % 2 === 0 ? 'info' : 'neutral', }); } }} size="sm" variant="secondary" > Queue stress </Button> <Button onPress={toast.dismissAll} size="sm" variant="ghost">Dismiss all</Button> </HStack> <Text tone="muted" variant="caption">{lastAction}</Text> </Section> </Card> );}
export function ComponentGallery({ onBack }: { onBack: () => void }) { const { theme } = useUniwind(); const [accepted, setAccepted] = React.useState(false); const [notifications, setNotifications] = React.useState(true); const [plan, setPlan] = React.useState<'starter' | 'pro'>('starter'); const [tab, setTab] = React.useState('overview'); const [otp, setOtp] = React.useState(''); const [filters, setFilters] = React.useState<string[]>(['mobile']); const [viewMode, setViewMode] = React.useState('list'); const [page, setPage] = React.useState(2); const [step, setStep] = React.useState(3); const [menuToolbar, setMenuToolbar] = React.useState(true); const [menuDensity, setMenuDensity] = React.useState('comfortable'); const [menuAction, setMenuAction] = React.useState('No action yet');
return ( <Screen testID="component-gallery"> <StatusBar barStyle={theme === 'dark' ? 'light-content' : 'dark-content'} /> <SafeArea className="bg-surface" edges={['top', 'left', 'right']}> <AppHeader description="Interactive React Native component playground built entirely from the public BeeUI API." testID="component-gallery-header" leading={ <HStack gap="sm"> <Button accessibilityLabel="Back to Showcase home" testID="component-gallery-back" onPress={onBack} size="sm" variant="ghost" > Back </Button> <Avatar accessibilityLabel="BeeUI" fallback="BU" /> </HStack> } title="Component Gallery" trailing={<ThemeToggle />} /> </SafeArea>
{/* #281 — `min-h-24` is a rem-scaled floor (matches the row/text scaling contract, so it keeps pace at any zoom) that stops this flex:1 scroll region from being squeezed toward zero when the pinned AppHeader above and BottomActionBar below both grow at large text / zoom. Without it, once combined header+bar height leaves less room than a single row needs, `scrollIntoView` can only partially reveal that row, and hit-testing at its (unclipped) geometric center lands on the BottomActionBar's real on-screen rect — reported as the bar "intercepting pointer events" for content that's actually just been squeezed out of its own viewport. The floor keeps the scroll region a real, usable size; on the rare device/zoom combination where total content still exceeds the viewport, the page falls back to ordinary document-level scrolling instead of corrupting hit-testing for in-view content. */} <SafeArea className="flex-1 min-h-24" edges={['left', 'right']}> <ScrollView contentContainerStyle={{ paddingBottom: 120 }}> <Box className="mx-auto w-full max-w-3xl gap-10 px-5 py-8"> <AlertBanner description="Everything below is rendered from the public @beemvp/beeui-ui API. Switch themes, open overlays, change form state, and resize the web window to exercise the same contracts used on native." title="Hands-on playground" variant="info" />
<PlaygroundHeading description="Buttons, semantic surfaces, feedback, and loading states."> Foundation </PlaygroundHeading>
<Card className="gap-4" variant="raised"> <Section action={<IconButton accessibilityLabel="Add item" variant="outline">+</IconButton>} description="Variants, disabled state, and loading behavior." title="Actions" > <Box className="gap-3"> <Button>Primary action</Button> <Button variant="secondary">Secondary action</Button> <Button variant="outline">Outline action</Button> <Button variant="ghost">Ghost action</Button> <Button variant="destructive">Destructive action</Button> <Button disabled>Disabled action</Button> <Button loading>Loading action</Button> </Box> </Section> </Card>
<Card className="gap-4" variant="muted"> <Text variant="heading">Status and feedback</Text> <Box className="flex-row flex-wrap gap-2"> <Badge>Primary</Badge> <Badge variant="secondary">Secondary</Badge> <Badge variant="success">Success</Badge> <Badge variant="warning">Warning</Badge> <Badge variant="destructive">Error</Badge> <Badge variant="info">Info</Badge> </Box> <Separator /> <Box className="flex-row items-center gap-5"> <Spinner /> <Spinner tone="success" /> <Spinner tone="warning" /> <Spinner tone="destructive" /> </Box> <Progress accessibilityLabel="Profile completion" value={72} /> </Card>
<ToastPlayground />
<Card className="gap-4"> <Text variant="heading">Loading and state surfaces</Text> <Box className="flex-row items-center gap-3"> <Skeleton className="h-12 w-12" variant="circle" /> <Box className="flex-1 gap-2"> <Skeleton className="w-2/3" variant="text" /> <Skeleton className="w-full" variant="text" /> </Box> </Box> <Skeleton className="h-24 w-full" /> <Separator /> <EmptyState action={<Button size="sm">Create record</Button>} description="Create your first record to get started." title="No records yet" /> <Separator /> <ErrorState action={<Button size="sm" variant="outline">Try again</Button>} description="The server could not load this section." /> </Card>
<PlaygroundHeading description="Text-entry composition plus explicit group semantics for related choices."> Forms </PlaygroundHeading>
<Card className="gap-4"> <Field description="Used only for account notifications." label="Email" required> <Input autoCapitalize="none" placeholder="you@example.com" /> </Field> <Field label="Search"> <SearchInput onSearch={() => undefined} placeholder="Search projects" /> </Field> <Field label="Password"> <PasswordInput placeholder="Enter password" /> </Field> <Field description="Six numeric digits." label="Verification code"> <OTPInput accessibilityLabel="Verification code" onValueChange={setOtp} value={otp} /> </Field> <Field error="Enter a valid project name." invalid label="Project name"> <Input placeholder="Invalid value" /> </Field> <Field disabled label="Managed field"> <Input placeholder="Disabled by field context" /> </Field> <Field description="Optional long-form content." label="Notes"> <Textarea placeholder="Long-form notes" /> </Field> <Separator /> <Checkbox checked={accepted} label="Accept terms" onCheckedChange={setAccepted} /> <FormGroup description="The group owns legend/guidance metadata while each radio stays independently discoverable." legend="Subscription plan" required > <RadioGroup onValueChange={(value) => setPlan(value as 'starter' | 'pro')} value={plan} > <Radio label="Starter plan" value="starter" /> <Radio label="Pro plan" value="pro" /> </RadioGroup> </FormGroup> <Box className="flex-row items-center justify-between gap-4"> <Text>Notifications</Text> <Switch accessibilityLabel="Notifications" onValueChange={setNotifications} value={notifications} /> </Box> </Card>
<PlaygroundHeading description="Persistent value selection with anchored overlay transport, keyboard behavior on web, and modal-local nesting."> Select </PlaygroundHeading>
<SelectShowcase />
<PlaygroundHeading description="Composable primitives (no owned data/columns) — real HTML table semantics on web, keyboard-reachable sort, and caller-owned row selection."> Table </PlaygroundHeading>
<TableShowcase />
<PlaygroundHeading description="Field-integrated trigger; Web opens Calendar in a Popover, native delegates to the system picker."> DatePicker </PlaygroundHeading>
<DatePickerShowcase />
<PlaygroundHeading description="Field-integrated trigger; composes DatePicker's date part with an Input/SegmentedControl time control on Web, native delegates to the system picker (chained date+time steps on Android)."> DateTimePicker </PlaygroundHeading>
<DateTimePickerShowcase />
<PlaygroundHeading description="Modal and anchored overlays now have real public APIs you can click through here."> Overlay playground </PlaygroundHeading>
<Card className="gap-5" variant="raised"> <Section description="Centered modal-class overlays use React Native core Modal." title="Dialog and AlertDialog" > <HStack gap="sm" wrap> <Dialog> <DialogTrigger>Open Dialog</DialogTrigger> <DialogContent> <DialogTitle>Project settings</DialogTitle> <DialogDescription> This is BeeUI's centered modal surface. Backdrop, accessibility escape, and native request-close follow the Dialog contract. </DialogDescription> <Field label="Project name"> <Input defaultValue="BeeUI" /> </Field> <DialogFooter> <DialogClose variant="outline">Cancel</DialogClose> <DialogClose>Save changes</DialogClose> </DialogFooter> </DialogContent> </Dialog>
<AlertDialog> <AlertDialogTrigger variant="destructive">Delete project</AlertDialogTrigger> <AlertDialogContent> <AlertDialogTitle>Delete this project?</AlertDialogTitle> <AlertDialogDescription> The backdrop cannot dismiss this confirmation. Choose an explicit action instead. </AlertDialogDescription> <AlertDialogFooter> <AlertDialogCancel>Cancel</AlertDialogCancel> <AlertDialogAction>Delete permanently</AlertDialogAction> </AlertDialogFooter> </AlertDialogContent> </AlertDialog> </HStack> </Section>
<Separator />
<Section description="Bottom-sheet surface (#159): BeeUI's own Web overlay/focus primitives — Escape, backdrop press, Tab focus-trap, and focus restoration — with no native Modal and no gorhom on Web (ADR-006)." title="Sheet" > <Sheet> <SheetTrigger testID="sheet-demo-trigger">Open Sheet</SheetTrigger> <SheetContent overlayTestID="sheet-demo-overlay" testID="sheet-demo-content"> <SheetTitle>Filters</SheetTitle> <SheetDescription>Refine results by category and price.</SheetDescription> <Field label="Search"> <Input accessibilityLabel="Sheet search" testID="sheet-demo-input" /> </Field> <SheetFooter> <SheetClose testID="sheet-demo-close" variant="outline"> Close </SheetClose> </SheetFooter> </SheetContent> </Sheet> </Section>
<Separator />
<Section description="Popover uses the shared non-Modal host, window-coordinate measurement, collision handling, and topmost dismissal stack." title="Popover placements" > <HStack gap="sm" wrap> <PlacementPopover placement="top" /> <PlacementPopover placement="right" /> <PlacementPopover placement="bottom" /> <PlacementPopover placement="left" /> </HStack> </Section>
<Separator />
<Section description="Menu items reuse the anchored runtime with normal, checkbox, radio, disabled, and keyboard-selection contracts." title="DropdownMenu" > <VStack gap="sm"> <DropdownMenu> <DropdownMenuTrigger variant="outline">Workspace menu</DropdownMenuTrigger> <DropdownMenuContent> <DropdownMenuLabel>Workspace</DropdownMenuLabel> <DropdownMenuItem onSelect={() => setMenuAction('Edit project')}> Edit project </DropdownMenuItem> <DropdownMenuItem disabled>Archive unavailable</DropdownMenuItem> <DropdownMenuSeparator /> <DropdownMenuCheckboxItem checked={menuToolbar} onCheckedChange={setMenuToolbar} > Show toolbar </DropdownMenuCheckboxItem> <DropdownMenuSeparator /> <DropdownMenuLabel>Density</DropdownMenuLabel> <DropdownMenuRadioGroup onValueChange={setMenuDensity} value={menuDensity} > <DropdownMenuRadioItem value="compact">Compact</DropdownMenuRadioItem> <DropdownMenuRadioItem value="comfortable"> Comfortable </DropdownMenuRadioItem> </DropdownMenuRadioGroup> </DropdownMenuContent> </DropdownMenu> <Text tone="muted"> {`Last action: ${menuAction} · toolbar ${menuToolbar ? 'on' : 'off'} · ${menuDensity}`} </Text> </VStack> </Section>
<Separator />
<Section description="Non-interactive contextual disclosure (#152/#153/#154): hover or focus reveals a short description after a delay; the trigger's own press action is never intercepted, Escape dismisses without moving focus, and content is never a Tab stop." title="Tooltip" > <HStack gap="sm" wrap> <Tooltip> <TooltipTrigger testID="tooltip-demo-trigger" variant="outline"> Autosave </TooltipTrigger> <TooltipContent testID="tooltip-demo-content"> Saved automatically every 30 seconds </TooltipContent> </Tooltip> </HStack> </Section>
<Separator />
<Section description="Open the parent, then the child. Outside press / Escape / accessibility escape dismisses child-first." title="Nested Popover" > <Popover> <PopoverTrigger variant="secondary">Open parent</PopoverTrigger> <PopoverContent align="start" placement="bottom"> <PopoverTitle>Parent Popover</PopoverTitle> <PopoverDescription> The child registers above this overlay in the shared dismiss stack. </PopoverDescription> <Popover> <PopoverTrigger size="sm" variant="outline">Open child</PopoverTrigger> <PopoverContent align="start" placement="right"> <PopoverTitle>Child Popover</PopoverTitle> <PopoverDescription> Dismiss me first; the parent remains mounted underneath. </PopoverDescription> <PopoverClose size="sm">Done</PopoverClose> </PopoverContent> </Popover> </PopoverContent> </Popover> </Section>
<Separator />
<Section description="This trigger is aligned to the container edge. Shrink the web viewport or rotate a device to make flip/shift collision handling obvious." title="Collision edge case" > <Box className="items-end"> <Popover> <PopoverTrigger variant="outline">Near right edge</PopoverTrigger> <PopoverContent align="start" placement="right" sideOffset={12}> <PopoverTitle>Collision-aware placement</PopoverTitle> <PopoverDescription> Preferred placement is right; the resolver flips or shifts only when the viewport requires it. </PopoverDescription> <PopoverClose size="sm" variant="ghost">Close</PopoverClose> </PopoverContent> </Popover> </Box> </Section>
<Separator />
<Section description="Consumer React context declared below BeeUIProvider resolves inside overlay content on web, native, and inside a Dialog." title="Consumer context" > <ConsumerContextOverlays /> </Section>
<Separator />
<Section description="BeeThemeScope (#68) is a thin typed wrapper over Uniwind's own ScopedTheme. The scoped brand/appearance below resolves for Popover, DropdownMenu, and Dialog content declared inside it, nests independently of an inner scope, and never leaks to the sibling value outside it." title="Scoped theme (BeeThemeScope)" > <ThemeScopeOverlays /> </Section> </Card>
<PlaygroundHeading description="Selections, tabs, disclosure, paging, and application-level composition stay router-neutral."> Navigation and composition </PlaygroundHeading>
<Card className="gap-4"> <Section description="Dependency-free filters, view selection, and paging." title="Selection and navigation"> <Text variant="label">Filters</Text> <ChipGroup onValueChange={(value) => setFilters(Array.isArray(value) ? value : [value])} selectionMode="multiple" value={filters} > <Chip value="mobile">Mobile</Chip> <Chip value="web">Web</Chip> <Chip value="design">Design</Chip> </ChipGroup> <Text variant="label">View</Text> <SegmentedControl onValueChange={setViewMode} value={viewMode}> <SegmentedControlItem value="list">List</SegmentedControlItem> <SegmentedControlItem value="grid">Grid</SegmentedControlItem> </SegmentedControl> <Text variant="label">Page</Text> <Pagination onPageChange={setPage} page={page} pageCount={4}> <PaginationItem type="previous" /> <PaginationItem page={1} /> <PaginationItem page={2} /> <PaginationItem page={3} /> <PaginationItem page={4} /> <PaginationItem type="next" /> </Pagination> </Section> </Card>
<Card className="gap-4"> <Text variant="heading">Tabs and disclosure</Text> <Tabs onValueChange={setTab} value={tab}> <TabsList> <TabsTrigger value="overview">Overview</TabsTrigger> <TabsTrigger value="details">Details</TabsTrigger> </TabsList> <TabsContent value="overview"> <Text tone="muted">Overview content is mounted for the active tab.</Text> </TabsContent> <TabsContent value="details"> <Text tone="muted">Details content is mounted only when selected.</Text> </TabsContent> </Tabs> <Separator /> <Collapsible> <CollapsibleTrigger>Advanced options</CollapsibleTrigger> <CollapsibleContent> <Text tone="muted">Hidden until expanded.</Text> </CollapsibleContent> </Collapsible> <Accordion defaultValue="account"> <AccordionItem value="account"> <AccordionTrigger>Account</AccordionTrigger> <AccordionContent> <Text tone="muted">Account preferences and identity.</Text> </AccordionContent> </AccordionItem> <AccordionItem value="billing"> <AccordionTrigger>Billing</AccordionTrigger> <AccordionContent> <Text tone="muted">Invoices and payment settings.</Text> </AccordionContent> </AccordionItem> </Accordion> </Card>
<Card className="gap-5"> <Section description="Layout, history, and application composition without router ownership." title="Application composition" > <Breadcrumb accessibilityLabel="Project breadcrumb"> <BreadcrumbItem onPress={() => undefined}>Projects</BreadcrumbItem> <BreadcrumbItem current>BeeUI</BreadcrumbItem> </Breadcrumb>
<Stack gap="lg"> <HStack gap="lg" wrap> <Stat className="min-w-32 flex-1"> <StatLabel>API surface</StatLabel> <StatValue>Public</StatValue> <StatHelpText>Only @beemvp/beeui-ui exports</StatHelpText> </Stat> <Stat className="min-w-32 flex-1"> <StatLabel>Verification</StatLabel> <StatValue>Cross-platform</StatValue> <StatHelpText>Web, Android, and iOS gates</StatHelpText> </Stat> </HStack>
<Stepper currentStep={step} onStepChange={setStep}> <StepperItem step={1} title="Foundation" /> <StepperItem step={2} title="Application patterns" /> <StepperItem step={3} title="Overlays" /> </Stepper>
<ListGroup> <ListGroupHeader description="Composition reuses existing row behavior." title="Workspace" /> <ListItem description="Portable component system" onPress={() => undefined} title="BeeUI" /> </ListGroup>
<Timeline> <TimelineItem description="Core layout, form, navigation, and modal contracts established." meta="v0.1" status="success" title="Foundation" /> <TimelineItem description="Packed packages bundle in Expo and bare React Native; Android APK compiles in CI." meta="CI verified" status="success" title="Native portability" /> <TimelineItem description="Public Popover and DropdownMenu exercise the shared anchored overlay geometry/runtime kernels." meta="Current" status="primary" title="Anchored overlays" /> </Timeline> </Stack> </Section> </Card>
<Card className="gap-4"> <Section description="Read-only application information patterns." title="Metadata and rows"> <DescriptionList> <DescriptionItem label="Runtime" value="React Native 0.86.2" /> <DescriptionItem label="Styling" value="Uniwind 1.10.1" /> <DescriptionItem description="Generated and compiled in CI" label="Native verification" value="Expo + bare RN" /> </DescriptionList> <Separator /> <ListItem description="Open your profile" onPress={() => undefined} title="Profile" trailing={<Badge variant="success">Active</Badge>} /> <SettingsItem description="Changes app appearance" onPress={() => undefined} title="Appearance" value={theme} /> <SettingsItem description="Native preference control" title="Push notifications" trailing={ <Switch accessibilityLabel="Push notifications" onValueChange={setNotifications} value={notifications} /> } /> </Section> </Card>
<VStack gap="xs"> <Text tone="muted" variant="caption"> Navigation remains application-owned. This showcase intentionally has no router or docs-site framework. </Text> <Link onPress={() => undefined}>Open documentation</Link> </VStack> </Box> </ScrollView> </SafeArea>
<SafeArea className="bg-surface" edges={['bottom', 'left', 'right']}> <BottomActionBar> <Button size="sm" variant="ghost">Cancel</Button> <Button size="sm">Save changes</Button> </BottomActionBar> </SafeArea> </Screen> );}Use the code block’s copy affordance to copy the exact fixture. For a smaller app-specific example, start from the public imports shown above and keep only the state your screen owns.
Limitations
Section titled “Limitations”No component-specific limitation is curated here. Check Compatibility and the linked behavior contract for target-specific constraints.