UTA DevHub
UI Development/UI Architecture/Foundation Components Guide

Interactive Elements

Foundation components that respond to user input - buttons, inputs, switches, and other interactive controls.

Interactive Elements

Overview

Interactive elements are foundation components that respond to user input such as taps, swipes, or keyboard actions. They form the basis of all user interactions in our applications and must be built with careful attention to accessibility, performance, and user experience.

Key Principles for Interactive Components

All interactive elements should:

  • Provide clear visual feedback for all states (default, hover, pressed, disabled)
  • Meet minimum touch target sizes (44×44 points)
  • Include comprehensive keyboard support
  • Communicate state changes to assistive technologies
  • Handle loading and error states gracefully

Button Components

The Button is perhaps the most fundamental interactive component in any UI system. It serves as the primary way users trigger actions in our applications.

📋 Complete Implementation Available

See our Button Component for a production-ready example with TypeScript types, accessibility features, testing patterns, and usage examples. Perfect for copy-pasting into your project!

Component Interface

// ui/foundation/Button/types.ts
import type { TouchableOpacityProps } from 'react-native';
 
export interface ButtonProps extends Omit<TouchableOpacityProps, 'style'> {
  /**
   * Visual style variant of the button
   * @default 'primary'
   */
  variant?: 'primary' | 'secondary' | 'ghost' | 'danger';
  
  /**
   * Size of the button
   * @default 'medium'
   */
  size?: 'small' | 'medium' | 'large';
  
  /**
   * When true, shows a loading spinner and disables interaction
   * @default false
   */
  loading?: boolean;
  
  /**
   * Optional icon to display before text
   */
  icon?: React.ReactNode;
  
  /**
   * Optional icon to display after text
   */
  iconRight?: React.ReactNode;
  
  /**
   * When true, button expands to fill its container width
   * @default false
   */
  fullWidth?: boolean;
  
  /**
   * Button content (typically text)
   */
  children: React.ReactNode;
  
  /**
   * Optional custom styles (use sparingly, prefer variants)
   */
  style?: ViewStyle;
}

Implementation Guidelines

Implementation Considerations

Buttons appear simple but require careful implementation to handle all scenarios effectively:

  • Accessibility: Ensure proper roles, states, and labels
  • Loading states: Prevent multiple submissions while actions are pending
  • Touch feedback: Provide appropriate visual feedback on press
  • Variants: Support different visual styles for different contexts
  • Composition: Allow composition with icons and other elements

Design Token Integration

Buttons should use design tokens for all visual properties:

// ui/foundation/Button/Button.tsx
import { theme } from '@/core/shared/styles/theme';
import { spacing } from '@/core/shared/styles/spacing';
import { typography } from '@/core/shared/styles/typography';
 
// Style implementation using design tokens
const getStyles = (variant: ButtonVariant, size: ButtonSize) => ({
  container: {
    backgroundColor: theme.colors[variant].background,
    borderRadius: theme.radii.md,
    padding: spacing[size],
    alignItems: 'center',
    justifyContent: 'center',
    flexDirection: 'row',
  },
  text: {
    color: theme.colors[variant].text,
    fontSize: typography.size[size],
    fontFamily: typography.families.medium,
  },
});

Loading State Implementation

Loading states should always:

  • Disable the button to prevent multiple submissions
  • Show a visual indicator (typically a spinner)
  • Maintain the button's size to prevent layout shifts
  • Provide appropriate accessibility updates

Button Best Practices

(Do ✅) Follow these Button component best practices

  • Use semantic colors based on button purpose (e.g., danger for destructive actions)
  • Include appropriate touch targets (minimum 44×44 points)
  • Maintain sufficient contrast ratios between text and background
  • Use consistent spacing between buttons when grouped
  • Always include a visible focus state for keyboard navigation
  • Provide clear, action-oriented button text

(Don't ❌) Make these common Button component mistakes

  • Using ambiguous or generic text like "Click Here" or "Submit"
  • Placing too many buttons with equal visual weight in proximity
  • Using buttons when links would be more appropriate (navigation vs. action)
  • Creating custom one-off button styles instead of using variants
  • Omitting loading states for asynchronous actions
  • Making touch targets too small for comfortable interaction

Complete Button Implementation

For a complete implementation of the Button component with all variants, states, accessibility features, and testing patterns, see the Button Component.

The Button example demonstrates:

  • ✅ All visual variants (primary, secondary, ghost, danger)
  • ✅ Size variations (small, medium, large)
  • ✅ Loading and disabled states
  • ✅ Icon support (left and right)
  • ✅ Full accessibility implementation
  • ✅ Design token integration
  • ✅ TypeScript interfaces
  • ✅ Comprehensive testing patterns

Input Components

Input components handle text entry and form interactions. They must balance functionality with accessibility and user experience across different platforms and input methods.

Input Interface

For a complete implementation of the Input component with all variants, validation, accessibility features, and testing patterns, see the Input Component Example.

The Input example demonstrates:

  • ✅ Multiple variants (outlined, filled)
  • ✅ Size variations (small, medium, large)
  • ✅ Validation with error messages
  • ✅ Helper text support
  • ✅ Icon support (left and right)
  • ✅ Focus state management
  • ✅ Full accessibility implementation
  • ✅ Comprehensive testing patterns

Input Best Practices

(Do ✅) Input component best practices

  • Provide clear labels for all inputs
  • Show validation feedback inline and immediately
  • Support autofill and password managers
  • Include helper text for complex inputs
  • Make error messages specific and actionable
  • Support platform-specific input behaviors

(Don't ❌) Common input mistakes

  • Validating only on form submission
  • Using placeholder text as the only label
  • Making error messages vague or technical
  • Ignoring platform keyboard differences
  • Forgetting to handle secure text entry
  • Using tiny touch targets for input fields

Switch Components

Switches provide binary on/off controls for settings and preferences.

Switch Interface

export interface SwitchProps {
  /**
   * Current value of the switch
   */
  value: boolean;
  
  /**
   * Called when switch value changes
   */
  onValueChange: (value: boolean) => void;
  
  /**
   * Disabled state
   * @default false
   */
  disabled?: boolean;
  
  /**
   * Size variant
   * @default 'medium'
   */
  size?: 'small' | 'medium' | 'large';
  
  /**
   * Color when switch is on
   * @default theme.colors.primary[500]
   */
  activeColor?: string;
  
  /**
   * Accessibility label
   */
  accessibilityLabel: string;
}

Switch Implementation Considerations

  • Visual feedback: Smooth animations for state changes
  • Touch targets: Ensure adequate size for easy interaction
  • Color contrast: Maintain visibility in both states
  • Platform consistency: Match iOS and Android patterns
  • Accessibility: Clear state announcements

Checkbox Components

Checkboxes allow users to select multiple options from a set.

Checkbox Interface

export interface CheckboxProps {
  /**
   * Whether checkbox is checked
   */
  checked: boolean;
  
  /**
   * Called when checkbox is pressed
   */
  onChange: (checked: boolean) => void;
  
  /**
   * Indeterminate state (partial selection)
   * @default false
   */
  indeterminate?: boolean;
  
  /**
   * Disabled state
   * @default false
   */
  disabled?: boolean;
  
  /**
   * Label text
   */
  label?: string;
  
  /**
   * Size variant
   * @default 'medium'
   */
  size?: 'small' | 'medium' | 'large';
  
  /**
   * Error state
   * @default false
   */
  error?: boolean;
}

Checkbox Best Practices

  • Support indeterminate state for parent-child relationships
  • Include clear touch targets beyond the checkbox itself
  • Provide visual feedback for all states
  • Support keyboard navigation (Space to toggle)
  • Group related checkboxes appropriately

Radio Button Components

Radio buttons allow single selection from a group of options.

RadioButton Interface

export interface RadioButtonProps {
  /**
   * Whether radio is selected
   */
  selected: boolean;
  
  /**
   * Called when radio is pressed
   */
  onPress: () => void;
  
  /**
   * Value of this radio option
   */
  value: string;
  
  /**
   * Display label
   */
  label: string;
  
  /**
   * Disabled state
   * @default false
   */
  disabled?: boolean;
  
  /**
   * Size variant
   * @default 'medium'
   */
  size?: 'small' | 'medium' | 'large';
}
 
export interface RadioGroupProps {
  /**
   * Currently selected value
   */
  value: string;
  
  /**
   * Called when selection changes
   */
  onChange: (value: string) => void;
  
  /**
   * Group name for accessibility
   */
  name: string;
  
  /**
   * Radio options
   */
  children: React.ReactNode;
}

Slider Components

Sliders allow users to select values from a continuous range.

Slider Interface

export interface SliderProps {
  /**
   * Current value
   */
  value: number;
  
  /**
   * Value change handler
   */
  onValueChange: (value: number) => void;
  
  /**
   * Minimum value
   * @default 0
   */
  minimumValue?: number;
  
  /**
   * Maximum value
   * @default 100
   */
  maximumValue?: number;
  
  /**
   * Step interval
   * @default 1
   */
  step?: number;
  
  /**
   * Show value label
   * @default false
   */
  showValueLabel?: boolean;
  
  /**
   * Disabled state
   * @default false
   */
  disabled?: boolean;
}

General Best Practices for Interactive Elements

Accessibility Requirements

All interactive elements must:

  1. Provide proper roles

    accessibilityRole="button"
    accessibilityRole="switch"
    accessibilityRole="checkbox"
  2. Communicate states

    accessibilityState={{
      disabled: isDisabled,
      checked: isChecked,
      selected: isSelected,
      busy: isLoading,
    }}
  3. Include descriptive labels

    accessibilityLabel="Save changes"
    accessibilityHint="Double tap to save your profile changes"

Performance Considerations

  • Use React.memo for components that receive stable props
  • Memoize style calculations with useMemo
  • Debounce rapid interactions when appropriate
  • Optimize animation performance with useNativeDriver

Testing Interactive Elements

// Example test structure
describe('Interactive Component', () => {
  it('responds to user interaction', () => {
    const onPress = jest.fn();
    const { getByRole } = render(
      <Button onPress={onPress}>Test</Button>
    );
    
    fireEvent.press(getByRole('button'));
    expect(onPress).toHaveBeenCalledTimes(1);
  });
  
  it('respects disabled state', () => {
    const onPress = jest.fn();
    const { getByRole } = render(
      <Button disabled onPress={onPress}>Test</Button>
    );
    
    fireEvent.press(getByRole('button'));
    expect(onPress).not.toHaveBeenCalled();
  });
});

Summary

Interactive elements form the foundation of user interaction in your application. By following these patterns and best practices, you can create components that are:

  • Accessible: Work for all users regardless of abilities
  • Performant: Respond quickly to user input
  • Consistent: Behave predictably across the application
  • Maintainable: Easy to update and extend

Next Steps