Pathfinderby Commenda
Components

Sheet

A floating record panel or bottom sheet with shared overlay contracts.

Import Sheet from @commenda/pathfinder/sheet.

API reference

PropTypeBehavior
sideConditional"right"|"bottom"right
size"sm"|"md"|"lg"md; right widths 400 / 520 / 720px
modalbooleanfalse; page stays usable and page clicks do not dismiss
dragConfigObjectView fieldsBottom only; true by default
titleRequiredstringRequired accessible title
descriptionReactNodeRequired accessible title
triggerReactElement<any, string | React.JSXElementConstructor<any>>Required accessible title
childrenRequiredReactNodeRequired accessible title
openConditionalbooleanControlled or uncontrolled
defaultOpenbooleanControlled or uncontrolled
onOpenChangeConditional(open: boolean) => voidControlled or uncontrolled
footerConfigOverlayFooterConfigView fieldsShared action/focus/guard contracts
dismissConfigOverlayDismissConfigView fieldsShared action/focus/guard contracts
focusConfigOverlayFocusConfigView fieldsShared action/focus/guard contracts
classNamestringShared action/focus/guard contracts
dragConfigObject fields
FieldTypeRequirement
dismissiblebooleanRequired
footerConfigObject fields
FieldTypeRequirement
primaryActionOverlayActionOptional
secondaryActionOverlayActionOptional
leadingContentReactNodeOptional
cancelLabelstringOptional
dismissConfigObject fields
FieldTypeRequirement
outsidePressbooleanOptional
beforeClose((reason: OverlayCloseReason) => boolean | Promise<boolean>)Optional
onError((error: unknown) => void)Optional
focusConfigObject fields
FieldTypeRequirement
initialFocusRefRefObject<HTMLElement | null>Optional
returnFocusRefRefObject<HTMLElement | null>Optional

open with onOpenChange is controlled; otherwise use defaultOpen. Primary actions never imply success or automatically close the surface. Close it after the application completes its work.

footerConfig accepts primaryAction, secondaryAction, leadingContent, and cancelLabel. Actions have label, onClick, native type/form, disabled, disabledReason, loadingConfig, destructive, and icons. Without a secondary action the footer renders Cancel. A footer is absent unless configured. Submit uses native form IDs; IDs must be unique.

Bottom sheet

The centered column is at most 640px; the sheet is at most 96dvh. Drag only the handle: a downward release over 0.4px/ms or distance over 25% of sheet height requests dismissal. Close, Escape, Cancel, and keyboard activation of the handle are equivalent alternatives. The body remains independently scrollable.

Modal mode couples the backdrop, focus containment, and page scroll lock. Outside dismissal remains off unless dismissConfig.outsidePress=true. Nonmodal sheets always ignore page clicks. Right sheets have a 16px inset and clamp to the viewport.

Unsaved changes

beforeClose also receives swipe. The application can show a ConfirmDialog and resolve the pending decision. Keep a single Sheet open, with at most its confirmation above it. The application owns request cancellation and should resolve outstanding confirmations if it removes the sheet. Read the overlay rules.

Design rules

Figma guidance and reviewed implementation decisions for this component. Rule IDs remain stable for source comments and AI references.

Sheet

  • SHEET-001: Right widths 400/520/720px, default 520, inset 16, viewport height minus 32. Bottom maximum 96dvh, pearl-100 surface, top radius12, centered640 column, handle48×6; safe-area bottom padding.
  • SHEET-002: Nonmodal by default, unlike Kakashi’s legacy default. Page remains usable and page clicks do not close it. Modal mode couples backdrop, focus containment, and scroll lock. Keep one Sheet, with its confirmation when necessary.
  • SHEET-003: Bottom handle only: distance over25% or downward release velocity over0.4px/ms, pointer capture, multi-pointer protection, cancel/reset. Body scroll does not drag. Guard swipe just like Close; keyboard, Escape, and Cancel alternatives remain available. Base UI Drawer1.9 uses a fixed0.5px/ms threshold and broader gesture handling, so the private gesture layer wraps Base UI Dialog rather than copying app logic.

Motion and edge cases

Occasional overlays use motion for spatial consistency. Motion paths and timings follow Kakashi: Dialog fades and moves from 95% scale with a 2% vertical offset in 200ms both ways; Popover fades from 95% scale and an 8px offset toward its trigger in 150ms both ways. Popover direction follows the resolved placement after collision handling. Right Sheet slides in for 500ms and out for 300ms; bottom Sheet slides for 500ms both ways. Sheet panels do not fade during normal motion, and modal backdrops follow the panel duration.

Pathfinder retains its foundation ease-out curve for Dialog/Popover and drawer curve (0.32,0.72,0,1) for Sheets. These are deliberate curve choices; this is not a copy of Kakashi’s legacy Radix keyframe implementation. Base UI continues to own presence, focus and dismissal. Transitions use a single transform value plus opacity where relevant, so Tailwind v4’s separate scale/translate properties cannot bypass interpolation. Closing or reopening mid-transition reverses from the current rendered position.

Keyboard opening and dismissal remain immediate, including Close and Cancel activation. Reduced motion removes scale and translation: Dialog/Popover keep their fade, while Sheets change position immediately and their modal backdrop can fade. Drag writes only the popup transform, never layout or inherited animation variables. Programmatic openings animate by default; explicit keyboard interactions suppress motion.

On this page