Design System

Data Entry Molecules

ImportCC

Available via @stackmates/ui-interactive/molecules

EditableText

Inline editable text that toggles between a clickable display span and an input field. Uses useOptimistic for instant feedback and useTransition for async action props.

Click the text below to edit:

Name:Project Alpha

Current value: Project Alpha

EditableTextProps

PropTypeDefaultDescription
value*stringThe current text value
action*(newValue: string) => void | Promise<void>Async action prop called with the new value on commit
displayValue(value: string) => ReactNodeCustom display renderer receiving the optimistic value
placeholderstring'Click to edit'Placeholder text when value is empty
classNamestringAdditional CSS classes

Installation

typescript
import { EditableText } from '@stackmates/ui-interactive/molecules';

Usage

Inline label edit with server action

tsx
<EditableText
  value={deal.name}
  action={async (newName) => {
    await updateDealName(deal.id, newName);
  }}
  placeholder="Untitled deal"
/>

Accessibility

  • Display span has role="button" and tabIndex={0} for keyboard activation
  • Enter key commits; Escape key cancels editing
  • data-pending attribute set during async transition for CSS styling
Related:

InputOTP

One-time password input with individual digit slots. Supports paste, arrow-key navigation, and numeric-only mode.

Entered: (empty)

InputOTPProps

PropTypeDefaultDescription
lengthnumber6Number of OTP digit slots
valuestringControlled OTP value
onChange(value: string) => voidCallback when value changes
maskbooleanfalseReplace digits with dots
type'text' | 'number''text'Use "number" to restrict to digits

Installation

typescript
import { InputOTP } from '@stackmates/ui-interactive/molecules';

Accessibility

  • role="group" with aria-label "One-time password input"
  • Each slot has aria-label "Digit N of M"
  • Paste support for full code entry
  • Arrow keys navigate between slots
Related:

SelectRx

Radix select dropdown with groups, labels, and separators. Rx suffix avoids conflict with atoms/Select.

Selected: (none)

SelectRx compound

PropTypeDefaultDescription
valuestringControlled selected value
onValueChange(value: string) => voidCallback when selection changes
defaultValuestringDefault value (uncontrolled)
disabledbooleanfalseDisable the trigger

Installation

typescript
import { SelectRx, SelectTriggerRx, SelectContentRx, SelectItemRx, SelectValueRx, SelectGroupRx, SelectLabelRx, SelectSeparatorRx } from '@stackmates/ui-interactive/molecules';

Accessibility

  • Full keyboard navigation (Arrow, Enter, Escape)
  • Check icon indicates current selection
  • Scroll buttons for overflowing content

RadioGroupRx

Radix radio group for mutually exclusive selection. Rx suffix avoids conflict with atoms/Radio.

Selected: comfortable

RadioGroupRx / RadioGroupItemRx

PropTypeDefaultDescription
valuestringControlled selected value
onValueChange(value: string) => voidCallback when selection changes
orientation'horizontal' | 'vertical''vertical'Layout direction
disabledbooleanfalseDisable all items

Installation

typescript
import { RadioGroupRx, RadioGroupItemRx } from '@stackmates/ui-interactive/molecules';

Accessibility

  • Proper role="radiogroup" via Radix
  • Arrow keys navigate, Space selects
  • loop (default true) wraps navigation

ToggleGroup

Grouped toggle buttons with single or multiple selection. Inherits Toggle atom variants via context.

Default variant

Outline variant

Active: center

ToggleGroup / ToggleGroupItem

PropTypeDefaultDescription
type*'single' | 'multiple'Single or multiple selection mode
valuestring | string[]Controlled active value(s)
variant'default' | 'outline''default'Visual variant from Toggle atom
size'default' | 'sm' | 'lg''default'Size variant from Toggle atom

Installation

typescript
import { ToggleGroup, ToggleGroupItem } from '@stackmates/ui-interactive/molecules';

Accessibility

  • type="single" enforces mutual exclusion
  • type="multiple" allows any combination
  • Use aria-label for icon-only items

Resizable

Split-panel layout with draggable handles. Built on react-resizable-panels.

Sidebar (40%)
Content (60%)

ResizablePanelGroup / ResizablePanel / ResizableHandle

PropTypeDefaultDescription
direction'horizontal' | 'vertical''horizontal'Split direction
defaultSizenumberPanel default size as percentage (ResizablePanel)
minSizenumberPanel minimum size as percentage (ResizablePanel)
withHandlebooleanfalseShow grip icon (ResizableHandle)

Installation

typescript
import { ResizablePanelGroup, ResizablePanel, ResizableHandle } from '@stackmates/ui-interactive/molecules';

Accessibility

  • Handle is focusable and keyboard-operable
  • withHandle adds visible grip for discoverability

Carousel

Content carousel powered by Embla Carousel. Supports looping, orientation, and keyboard navigation.

Slide 1
Slide 2
Slide 3
Slide 4

CarouselProps

PropTypeDefaultDescription
orientation'horizontal' | 'vertical''horizontal'Scroll direction
optsCarouselOptionsEmbla options (loop, align, etc.)
pluginsCarouselPluginEmbla plugins (autoplay, etc.)
setApi(api: CarouselApi) => voidReceive Embla API for programmatic control

Installation

typescript
import { Carousel, CarouselContent, CarouselItem, CarouselPrevious, CarouselNext } from '@stackmates/ui-interactive/molecules';

Accessibility

  • role="region" with aria-roledescription="carousel"
  • Slides have role="group" + aria-roledescription="slide"
  • Arrow keys scroll, nav buttons have sr-only labels

Listbox

Headless UI listbox for selecting from a dropdown list. Shows selected value inline.

Selected: (none)

Listbox / ListboxOption / ListboxLabel

PropTypeDefaultDescription
valueTControlled selected value
onChange(value: T) => voidCallback when selection changes
placeholderReactNodePlaceholder when nothing selected
aria-labelstringAccessible label for the trigger

Installation

typescript
import { Listbox, ListboxOption, ListboxLabel, ListboxDescription } from '@stackmates/ui-interactive/molecules';

Accessibility

  • Headless UI Listbox with proper ARIA roles
  • Arrow keys + Enter for selection
  • Check mark on selected option

Combobox

Searchable dropdown built on Headless UI Combobox. Type to filter options with custom filtering, virtual scrolling, and render-prop children for option rendering.

Selected: (none)

ComboboxProps<T>

PropTypeDefaultDescription
options*T[]Array of options to display
displayValue*(value: T | null) => string | undefinedExtract display string from an option
filter(value: T, query: string) => booleanCustom filter. Falls back to displayValue includes check.
placeholderstringPlaceholder text for the input
children*(value: T) => ReactElementRender function for each option row

Installation

typescript
import { Combobox, ComboboxOption, ComboboxLabel, ComboboxDescription } from '@stackmates/ui-interactive/molecules';

Accessibility

  • Headless UI ARIA combobox pattern with aria-label support
  • Arrow keys + Enter for keyboard navigation and selection
  • Selected option shows check icon indicator

Command

Command palette built on cmdk. Compound component with search input, grouped items, empty state, and keyboard shortcuts. Use CommandDialog for modal overlay variant.

Command

PropTypeDefaultDescription
children*ReactNodeCommandInput, CommandList, CommandGroup, CommandItem composition
classNamestringAdditional CSS classes on the Command root

Installation

typescript
import { Command, CommandDialog, CommandInput, CommandList, CommandGroup, CommandItem, CommandEmpty, CommandSeparator, CommandShortcut } from '@stackmates/ui-interactive/molecules';

Accessibility

  • Built on cmdk with full keyboard navigation and auto-filtering
  • Search input auto-focused inside CommandDialog
  • Items support aria-selected state