Design System

Form Primitives

ImportCC

Available via @stackmates/ui-interactive/atoms

Input

Text input field with consistent styling. HeadlessUI-based with focus ring and validation states.

InputProps

PropTypeDefaultDescription
type'text' | 'email' | 'password' | 'number' | 'search' | 'tel' | 'url' | 'date''text'HTML input type
placeholderstringPlaceholder text
disabledbooleanfalseDisable the input
classNamestringAdditional CSS classes

Installation

typescript
import { Input } from '@stackmates/ui-interactive/atoms';

Accessibility

  • Built on HeadlessUI Input primitive with native focus management
  • Supports data-invalid attribute for error state styling
  • Pair with Label for accessible form fields

Checkbox

Radix checkbox with check and indeterminate indicators.

CheckboxProps

PropTypeDefaultDescription
checkedboolean | 'indeterminate'Controlled checked state
onCheckedChange(checked: boolean) => voidChange handler
disabledbooleanfalseDisable the checkbox

Installation

typescript
import { Checkbox } from '@stackmates/ui-interactive/atoms';

Accessibility

  • Built on Radix Checkbox with full keyboard support (Space to toggle)
  • Supports indeterminate state for partial selections
  • Focus ring visible on keyboard navigation
Related:

Label

Form label element with optional required indicator. Built on Radix Label primitive.

LabelProps

PropTypeDefaultDescription
children*ReactNodeLabel text content
requiredbooleanfalseShow red asterisk indicator
htmlForstringAssociated input ID

Installation

typescript
import { Label } from '@stackmates/ui-interactive/atoms';

Accessibility

  • Renders as Radix Label with automatic htmlFor association
  • Required asterisk is decorative — pair with aria-required on the input
  • Responds to disabled state via peer/group CSS selectors

Textarea

Multi-line text input with optional resize control. HeadlessUI-based.

TextareaProps

PropTypeDefaultDescription
placeholderstringPlaceholder text
resizablebooleantrueAllow vertical resize
disabledbooleanfalseDisable the textarea
rowsnumberVisible text rows

Installation

typescript
import { Textarea } from '@stackmates/ui-interactive/atoms';

Accessibility

  • Built on HeadlessUI Textarea with native focus management
  • Supports data-invalid for error states
  • Pair with Label for accessible form fields
Related:

Switch

Toggle switch with 20+ color variants. HeadlessUI-based with smooth transitions.

SwitchProps

PropTypeDefaultDescription
checkedbooleanControlled on/off state
onChange(checked: boolean) => voidChange handler
colorColor'dark/zinc'Color theme when checked
disabledbooleanfalseDisable the switch

Installation

typescript
import { Switch } from '@stackmates/ui-interactive/atoms';

Accessibility

  • Built on HeadlessUI Switch with role="switch" and aria-checked
  • Keyboard accessible via Space to toggle
  • Focus ring visible with outline offset

Toggle

Pressable toggle button with variant and size options. CVA-styled.

ToggleProps

PropTypeDefaultDescription
pressedbooleanControlled pressed state
variant'default' | 'outline''default'Visual variant
size'default' | 'sm' | 'lg''default'Toggle size

Installation

typescript
import { Toggle } from '@stackmates/ui-interactive/atoms';

Accessibility

  • Uses aria-pressed to communicate toggle state
  • Renders as native <button> with keyboard support
  • data-state attribute provides CSS hook for on/off styling
Related:

Slider

Range input with styled thumb and track. Supports controlled and uncontrolled usage with custom min/max/step.

50%

SliderProps

PropTypeDefaultDescription
valuenumberCurrent value (controlled)
defaultValuenumberDefault value for uncontrolled usage
minnumber0Minimum value
maxnumber100Maximum value
stepnumber1Step increment between values
onValueChange(value: number) => voidCalled when value changes

Installation

typescript
import { Slider } from '@stackmates/ui-interactive/atoms';

Usage

Controlled with display

tsx
const [value, setValue] = useState(50);
<Slider value={value} onValueChange={setValue} />

Custom range

tsx
<Slider min={0} max={10} step={0.5} defaultValue={5} />

Accessibility

  • Renders as native <input type="range"> for full browser accessibility
  • Focus-visible ring with offset for keyboard navigation
  • Disabled state uses cursor-not-allowed and reduced opacity
  • Pair with Label for accessible form fields
Related:

CheckboxTW

HeadlessUI checkbox with 20+ color variants, indeterminate support, and CheckboxGroup/CheckboxField layout helpers.

CheckboxTWProps

PropTypeDefaultDescription
colorColor'dark/zinc'Color theme when checked (20+ color options including dark/zinc, blue, green, red, etc.)
checkedbooleanControlled checked state
onChange(checked: boolean) => voidChange handler
disabledbooleanfalseDisable the checkbox
classNamestringAdditional CSS classes

Installation

typescript
import { CheckboxTW, CheckboxField, CheckboxGroup } from '@stackmates/ui-interactive/atoms';

Usage

With field layout

tsx
<CheckboxGroup>
  <CheckboxField>
    <CheckboxTW checked={checked} onChange={setChecked} color="blue" />
    <Label>Accept terms</Label>
  </CheckboxField>
</CheckboxGroup>

Color variants

tsx
<CheckboxTW color="green" checked onChange={handleChange} />
<CheckboxTW color="red" checked onChange={handleChange} />

Accessibility

  • Built on HeadlessUI Checkbox with full keyboard support (Space to toggle)
  • CheckboxField provides grid layout linking label to control via data-slot attributes
  • Focus ring uses outline-offset-2 with blue-500 for clear keyboard navigation visibility
  • Supports forced-colors mode for Windows high contrast

SwitchRx

Radix-based toggle switch with async action prop for optimistic server mutations.

SwitchRxProps

PropTypeDefaultDescription
checkedbooleanControlled checked state
onCheckedChange(checked: boolean) => voidChange handler (Radix convention)
action(checked: boolean) => void | Promise<void>Async action prop — uses useOptimistic for instant flip, auto-reverts on failure
disabledbooleanfalseDisable the switch
classNamestringAdditional CSS classes

Installation

typescript
import { Switch } from '@stackmates/ui-interactive/atoms';
// Note: Exported as "Switch" from the SwitchRx module

Usage

Controlled

tsx
const [enabled, setEnabled] = useState(false);
<Switch checked={enabled} onCheckedChange={setEnabled} />

With async action (optimistic)

tsx
<Switch
  checked={feature.enabled}
  action={async (checked) => {
    await toggleFeatureAction(feature.id, checked);
  }}
/>

Accessibility

  • Built on Radix Switch with role="switch" and aria-checked state
  • Keyboard accessible via Space to toggle checked state
  • Focus ring uses 3px ring with ring-ring/50 for clear visibility
  • data-pending attribute set during async transitions for CSS styling hooks

TextareaRx

Minimal textarea with field-sizing-content for auto-grow, aria-invalid styling, and dark mode support.

TextareaRxProps

PropTypeDefaultDescription
placeholderstringPlaceholder text
disabledbooleanfalseDisable the textarea
rowsnumberVisible text rows
classNamestringAdditional CSS classes
aria-invalidbooleanMarks the textarea as invalid with destructive ring styling

Installation

typescript
import { Textarea } from '@stackmates/ui-interactive/atoms';
// Note: Exported as "Textarea" from the TextareaRx module

Usage

Basic

tsx
<Textarea placeholder="Enter description..." />

With validation error

tsx
<Textarea
  placeholder="Required field"
  aria-invalid={!!errors.description}
/>
<ErrorMessage message={errors.description} />

Accessibility

  • Uses field-sizing-content CSS property for content-driven auto-grow without JavaScript
  • aria-invalid triggers destructive ring styling for clear error indication
  • Focus ring uses 3px ring with border-ring transition for smooth keyboard navigation
Related: