Skip to main content

Tooltip Component Design and Implementation

Comprehensive low-level design for a reusable, accessible tooltip component with dynamic positioning and performance optimizations

This document presents a production-ready tooltip component design focusing on accessibility, dynamic positioning, and performance optimizations for modern web applications.

1. Problem Statement & Requirements

Clarifying questions

  1. What target browsers/devices should this tooltip support? (e.g., mobile touch, desktop hover, screen readers)
  2. Should tooltips be accessible via keyboard navigation or only mouse interactions?
  3. Do we need real-time updates or can tooltips be static once rendered?
  4. Should tooltips work offline or rely on network connectivity for content?
  5. What level of accessibility compliance is required? (e.g., WCAG 2.1 AA, full ARIA support)
  6. Do we need internationalization support for multi-language tooltips?
  7. Are there existing design system components or UI libraries we should integrate with?

Functional requirements

  • Display contextual help text on hover or focus
  • Support multiple trigger methods (hover, click, focus)
  • Show/hide with appropriate animations or transitions
  • Position tooltip relative to trigger element (top, bottom, left, right)
  • Handle overflow and boundary detection for positioning
  • Support rich content (HTML, markdown, or React elements)
  • Allow custom styling and theme integration
  • Support for keyboard navigation and screen reader accessibility

Non-functional requirements

  • Performance: Tooltip should not cause layout shifts or jank
  • Accessibility: Must meet WCAG 2.1 AA standards for keyboard navigation and screen readers
  • Scalability: Should handle large numbers of tooltips efficiently
  • Maintainability: Component should be easily customizable and extendable
  • Bundle size: Should not significantly increase frontend bundle size

Constraints and assumptions

  • Component will be used within a React application with TypeScript
  • Target modern browsers (Chrome 80+, Firefox 70+, Safari 13+)
  • No external dependencies beyond React and standard DOM APIs
  • Tooltip content is assumed to be safe HTML or text (no XSS concerns in this scope)
  • Positioning logic will use DOM Rect calculations for accuracy
  • Component assumes proper CSS styling and theming is provided

Out of scope

  • Server-side rendering (SSR) considerations
  • Complex animations beyond basic show/hide transitions
  • Tooltip content pre-fetching or dynamic loading from external sources
  • Multi-language translation integration (i18n)
  • Tooltip persistence across page navigation or browser sessions
  • Custom tooltip themes beyond basic styling options

2. Component Architecture

graph TD
    A[Tooltip] --> B[TooltipTrigger]
    A --> C[TooltipContent]
    B --> D[TooltipPortal]
    C --> E[TooltipArrow]
    D --> F[TooltipRoot]
    
    classDef smart fill:#4CAF50,stroke:#333,color:white;
    classDef dumb fill:#2196F3,stroke:#333,color:white;
    
    class A,B,C,E,F smart
    class D dumb

Component Tree Structure

The Tooltip component is composed of several atomic components:

  1. TooltipRoot - The root container that manages the tooltip lifecycle and state
  2. TooltipTrigger - The element that activates the tooltip on hover/focus
  3. TooltipPortal - Renders the tooltip content outside the DOM tree to avoid z-index issues
  4. TooltipContent - The actual tooltip content container with positioning logic
  5. TooltipArrow - A small arrow that points to the trigger element

Component Breakdown

Smart Components

  • TooltipRoot: Container that manages state, handles events, and provides context to child components
  • TooltipTrigger: Handles hover/focus interactions and manages the tooltip lifecycle

Dumb Components

  • TooltipPortal: Renders content in a separate DOM node using React Portal
  • TooltipContent: Pure presentation layer with positioning logic
  • TooltipArrow: Simple arrow component that renders a triangle

TypeScript Props Contracts

// TooltipTrigger Props
interface TooltipTriggerProps {
  children: React.ReactNode;
  asChild?: boolean;
  disabled?: boolean;
}

// TooltipContent Props
interface TooltipContentProps {
  children: React.ReactNode;
  side?: 'top' | 'right' | 'bottom' | 'left';
  sideOffset?: number;
  align?: 'start' | 'center' | 'end';
  alignOffset?: number;
  avoidCollisions?: boolean;
  collisionBoundary?: Element | null;
  className?: string;
}

// TooltipRoot Props
interface TooltipRootProps {
  children: React.ReactNode;
  open?: boolean;
  defaultOpen?: boolean;
  onOpenChange?: (open: boolean) => void;
  delayMs?: number;
  disableHoverableContent?: boolean;
}

// TooltipArrow Props
interface TooltipArrowProps {
  className?: string;
  width?: number;
  height?: number;
}

UI State Machine

stateDiagram-v2
    [*] --> Closed
    Closed --> Opening : hoverEnter
    Opening --> Open : delayMs
    Open --> Closing : hoverLeave
    Closing --> Closed : delayMs
    Open --> Closing : focusOut
    Closing --> Closed : focusOut
    Closed --> Opening : focusIn
    Opening --> Open : delayMs
    Open --> Closing : blur
    Closing --> Closed : blur

Component Responsibilities

  1. TooltipRoot:

    • Manages global tooltip state and context
    • Handles delay timing for opening/closing
    • Provides state to child components via React Context
  2. TooltipTrigger:

    • Wraps the trigger element that activates the tooltip
    • Handles hover/focus events with proper timing delays
    • Manages accessibility attributes (aria-describedby)
  3. TooltipPortal:

    • Renders content outside the normal DOM flow
    • Ensures proper z-index handling and positioning
  4. TooltipContent:

    • Renders the actual tooltip content
    • Handles positioning logic based on trigger element
    • Manages collision detection and boundary constraints
  5. TooltipArrow:

    • Renders an arrow pointing to the trigger element
    • Handles orientation and positioning

Reusability Decisions

The Tooltip component is designed to be highly reusable across the application:

  1. Positioning flexibility: Supports all four sides with customizable offsets
  2. Accessibility-first: Implements proper ARIA attributes and keyboard navigation
  3. Styling agnostic: Accepts className prop for easy theming
  4. Context-aware: Uses React Context to maintain state across nested tooltips

Component Lifecycle Considerations

  1. Mounting: TooltipPortal mounts the content to a separate DOM node
  2. Updating: Position recalculated on trigger element size changes and window resize
  3. Unmounting: Cleanup of event listeners and DOM node removal
  4. State transitions: Delayed open/close behavior to prevent flickering
  5. Focus management: Proper focus trapping and restoration when tooltips are active

The component architecture follows React best practices with proper separation of concerns, allowing for easy testing and maintenance while providing a flexible API that can be extended for various use cases.

3. State Management Design

Local state vs. global state decisions

For a Tooltip component, local state is sufficient for most use cases. The tooltip's visibility and positioning are inherently tied to the component that triggers it, making global state unnecessary. We'll use React's built-in useState for managing the tooltip's open/closed state and position calculations.

State management approach

We'll use React's built-in state management with useState and useEffect hooks. This approach is optimal because:

  • Tooltips are typically lightweight and context-specific
  • No complex cross-component state sharing is required
  • Reduces dependency on external libraries
  • Maintains good performance with minimal re-renders

State shape and normalization strategy

interface TooltipState {
  isOpen: boolean;
  position: {
    top: number;
    left: number;
    right: number;
    bottom: number;
  };
  placement: 'top' | 'bottom' | 'left' | 'right';
}

Server state vs. client state separation

Tooltips are purely client-side UI components with no server state dependencies. All positioning and visibility logic is handled locally.

Cache invalidation strategy

Since tooltips don't cache data, no cache invalidation strategy is needed.

Optimistic updates design

Tooltips don't require optimistic updates as they're purely UI elements without data mutations.

Critical-path hook implementation

import { useState, useEffect, useRef } from 'react';

interface UseTooltipProps {
  placement?: 'top' | 'bottom' | 'left' | 'right';
  offset?: number;
  delay?: number;
}

interface UseTooltipReturn {
  isOpen: boolean;
  position: {
    top: number;
    left: number;
    right: number;
    bottom: number;
  };
  showTooltip: () => void;
  hideTooltip: () => void;
  toggleTooltip: () => void;
}

const useTooltip = ({ 
  placement = 'top', 
  offset = 8,
  delay = 200 
}: UseTooltipProps): UseTooltipReturn => {
  const [isOpen, setIsOpen] = useState(false);
  const [position, setPosition] = useState({
    top: 0,
    left: 0,
    right: 0,
    bottom: 0
  });
  
  const triggerRef = useRef<HTMLDivElement>(null);
  const tooltipRef = useRef<HTMLDivElement>(null);
  const timeoutRef = useRef<NodeJS.Timeout | null>(null);

  const calculatePosition = () => {
    if (!triggerRef.current || !tooltipRef.current) return;

    const triggerRect = triggerRef.current.getBoundingClientRect();
    const tooltipRect = tooltipRef.current.getBoundingClientRect();
    
    let top = 0;
    let left = 0;

    switch (placement) {
      case 'top':
        top = triggerRect.top - tooltipRect.height - offset;
        left = triggerRect.left + (triggerRect.width / 2) - (tooltipRect.width / 2);
        break;
      case 'bottom':
        top = triggerRect.bottom + offset;
        left = triggerRect.left + (triggerRect.width / 2) - (tooltipRect.width / 2);
        break;
      case 'left':
        top = triggerRect.top + (triggerRect.height / 2) - (tooltipRect.height / 2);
        left = triggerRect.left - tooltipRect.width - offset;
        break;
      case 'right':
        top = triggerRect.top + (triggerRect.height / 2) - (tooltipRect.height / 2);
        left = triggerRect.right + offset;
        break;
    }

    setPosition({
      top,
      left,
      right: window.innerWidth - (left + tooltipRect.width),
      bottom: window.innerHeight - (top + tooltipRect.height)
    });
  };

  const showTooltip = () => {
    if (timeoutRef.current) {
      clearTimeout(timeoutRef.current);
    }
    
    timeoutRef.current = setTimeout(() => {
      setIsOpen(true);
      calculatePosition();
    }, delay);
  };

  const hideTooltip = () => {
    if (timeoutRef.current) {
      clearTimeout(timeoutRef.current);
    }
    
    setIsOpen(false);
  };

  const toggleTooltip = () => {
    if (isOpen) {
      hideTooltip();
    } else {
      showTooltip();
    }
  };

  useEffect(() => {
    if (isOpen) {
      calculatePosition();
      window.addEventListener('resize', calculatePosition);
      window.addEventListener('scroll', calculatePosition);
    }

    return () => {
      if (timeoutRef.current) {
        clearTimeout(timeoutRef.current);
      }
      window.removeEventListener('resize', calculatePosition);
      window.removeEventListener('scroll', calculatePosition);
    };
  }, [isOpen, placement]);

  return {
    isOpen,
    position,
    showTooltip,
    hideTooltip,
    toggleTooltip
  };
};

export default useTooltip;

The hook returns:

  • isOpen: Boolean indicating tooltip visibility
  • position: Object containing calculated positioning values
  • showTooltip, hideTooltip, toggleTooltip: Functions to control tooltip state

Key state transitions:

  1. showTooltip → sets isOpen to true after delay and calculates position
  2. hideTooltip → sets isOpen to false immediately
  3. toggleTooltip → toggles between show/hide states

Side effects handled:

  • Window resize and scroll event listeners for position recalculations
  • Cleanup of timeouts and event listeners to prevent memory leaks
  • Delayed execution for hover interactions to avoid flickering

4. Routing & Navigation

Route Structure and Hierarchy

The tooltip component is a presentational UI element that does not require dedicated routing. It's typically rendered as part of other components or pages and doesn't have its own route in the application's URL structure.

// Route structure would look like:
/
  /dashboard
    /analytics
    /reports
  /profile
  /settings

Code Splitting Strategy

Since tooltips are lightweight UI components that are typically rendered conditionally within existing routes, code splitting is not applicable at the route level. However, tooltip components could be imported dynamically when needed:

// Example of dynamic import for tooltip component (if needed)
const Tooltip = React.lazy(() => import('./components/Tooltip'));

Tooltips themselves do not participate in navigation guards or authentication flows. They are rendered conditionally based on user interaction and do not affect routing logic.

Deep Linking Support and URL Parameter Design

Tooltips don't require deep linking support or URL parameter handling since they're UI elements that appear on demand. They are typically triggered by hover, click, or focus events rather than URL states.

Browser History Management and Scroll Restoration

Browser history management is not affected by tooltip usage. The component does not modify the browser's history stack or require special scroll restoration handling since it's a transient UI element that appears and disappears based on user interaction.

Component Integration Considerations

The tooltip component should integrate cleanly with existing navigation patterns:

  1. Positioning: Tooltips must be positioned relative to their trigger elements without interfering with navigation
  2. Focus Management: When tooltips appear via keyboard focus, proper focus restoration must be maintained
  3. Accessibility: Tooltips should not break existing keyboard navigation flows or screen reader experiences
// Example integration pattern in a page component:
const MyPage = () => {
  return (
    <div>
      <button 
        aria-describedby="tooltip-1"
        onMouseEnter={() => setShowTooltip(true)}
        onFocus={() => setShowTooltip(true)}
      >
        Hover me
      </button>
      
      {showTooltip && (
        <Tooltip 
          id="tooltip-1"
          content="This is a tooltip"
          position="top"
        />
      )}
    </div>
  );
};

The component architecture ensures that tooltips work seamlessly with existing routing patterns without requiring any special route handling or navigation considerations.

5. API Integration Layer

API client design pattern

For a tooltip component, we don't typically integrate with external APIs since tooltips are usually static content or dynamically generated from local data. However, if the tooltip needs to fetch dynamic content (e.g., user profile info, product details), we would use a centralized API client.

// api/client.ts
import axios, { AxiosInstance } from 'axios';

const createApiClient = (): AxiosInstance => {
  const client = axios.create({
    baseURL: process.env.NEXT_PUBLIC_API_BASE_URL || 'https://api.example.com',
    timeout: 10000,
    headers: {
      'Content-Type': 'application/json',
    },
  });

  // Request interceptor for auth token injection
  client.interceptors.request.use(
    (config) => {
      const token = localStorage.getItem('authToken');
      if (token) {
        config.headers.Authorization = `Bearer ${token}`;
      }
      return config;
    },
    (error) => Promise.reject(error)
  );

  // Response interceptor for error handling
  client.interceptors.response.use(
    (response) => response,
    (error) => {
      if (error.response?.status === 401) {
        // Handle auth errors
        localStorage.removeItem('authToken');
        window.location.href = '/login';
      }
      return Promise.reject(error);
    }
  );

  return client;
};

export const apiClient = createApiClient();

Request/response interceptors

The API client handles authentication tokens and error responses. For tooltip content, we would typically have:

  • Request Interceptor: Adds auth tokens for protected endpoints
  • Response Interceptor: Handles 401 errors and normalizes responses

Error handling and retry strategy

For tooltip content, we implement a robust error handling system:

// hooks/useTooltipContent.ts
import { useState, useEffect } from 'react';
import { apiClient } from '../api/client';

interface TooltipContent {
  id: string;
  title: string;
  description: string;
  metadata?: Record<string, any>;
}

interface UseTooltipContentResult {
  content: TooltipContent | null;
  loading: boolean;
  error: Error | null;
  refetch: () => void;
}

const useTooltipContent = (id: string): UseTooltipContentResult => {
  const [content, setContent] = useState<TooltipContent | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<Error | null>(null);

  const fetchContent = async () => {
    setLoading(true);
    setError(null);
    
    try {
      const response = await apiClient.get<TooltipContent>(`/tooltip/${id}`);
      setContent(response.data);
    } catch (err) {
      setError(err instanceof Error ? err : new Error('Failed to fetch tooltip content'));
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => {
    if (id) {
      fetchContent();
    }
  }, [id]);

  return {
    content,
    loading,
    error,
    refetch: fetchContent,
  };
};

export default useTooltipContent;

Loading, error, and empty states design

The tooltip component handles these states gracefully:

// components/Tooltip.tsx
import React, { useState, useEffect } from 'react';
import useTooltipContent from '../hooks/useTooltipContent';

interface TooltipProps {
  id: string;
  children: React.ReactNode;
  delay?: number;
}

const Tooltip: React.FC<TooltipProps> = ({ id, children, delay = 300 }) => {
  const [isVisible, setIsVisible] = useState(false);
  const [position, setPosition] = useState({ top: 0, left: 0 });
  const { content, loading, error } = useTooltipContent(id);

  const handleMouseEnter = () => {
    setTimeout(() => setIsVisible(true), delay);
  };

  const handleMouseLeave = () => {
    setIsVisible(false);
  };

  return (
    <div 
      className="tooltip-trigger"
      onMouseEnter={handleMouseEnter}
      onMouseLeave={handleMouseLeave}
    >
      {children}
      {isVisible && (
        <div 
          className="tooltip-content"
          style={{ top: position.top, left: position.left }}
        >
          {loading && <div>Loading...</div>}
          {error && <div className="error">Failed to load content</div>}
          {content && (
            <div>
              <h3>{content.title}</h3>
              <p>{content.description}</p>
            </div>
          )}
        </div>
      )}
    </div>
  );
};

export default Tooltip;

Data fetching strategy

For static tooltip content, we use:

  • CSR (Client-Side Rendering) - For dynamic content that requires user interaction
  • SSR (Server-Side Rendering) - For SEO-critical tooltip content that's part of the page structure

API response transformation layer

// api/transformers.ts
import { TooltipContent } from '../types';

export const transformTooltipResponse = (response: any): TooltipContent => {
  return {
    id: response.id,
    title: response.title || '',
    description: response.description || '',
    metadata: response.metadata || {},
  };
};

Sequence diagram for primary data flows

sequenceDiagram
    participant User
    participant TooltipComponent
    participant Hook
    participant API
    participant Store

    User->>TooltipComponent: Hover over tooltip trigger
    TooltipComponent->>Hook: Fetch content (with delay)
    Hook->>API: GET /tooltip/{id}
    API-->>Hook: Response with content
    Hook->>TooltipComponent: Update state with content
    TooltipComponent->>User: Display tooltip with content

6. Performance Design

Core Web Vitals targets

  • Largest Contentful Paint (LCP): <2.5s for tooltip content rendering
  • Interaction to Next Paint (INP): <200ms for tooltip show/hide interactions
  • Cumulative Layout Shift (CLS): <0.1 for tooltip positioning and content layout

Bundle size budget per route

  • Tooltip component: <2KB (minified + gzipped)
  • Tooltip dependencies (positioning library): <10KB (minified + gzipped)
  • Total tooltip feature bundle: <15KB (including all dependencies)

Critical rendering path optimization

  • Tooltip content should be rendered with minimal DOM overhead
  • Use CSS transforms for positioning instead of absolute positioning calculations to avoid layout thrashing
  • Tooltip content should not block rendering of main content
  • Implement CSS containment for tooltip elements to prevent style recalculation cascades

Image optimization strategy

  • Tooltip content may include images, so ensure proper lazy loading via loading="lazy" attribute
  • Support responsive image formats (webp where available) for any images in tooltip content
  • Implement proper sizes and srcset attributes for responsive image delivery

Font loading strategy

  • Tooltip content should use system fonts to avoid font loading delays
  • Implement font fallbacks for accessibility and performance
  • Use font-display: swap to prevent invisible text during font loading

Memoization strategy

  • Memoize: Tooltip content rendering, positioning calculations, and event handlers
  • Avoid memoization: Simple state updates (show/hide), DOM ref access, and basic event binding
  • Use useCallback for tooltip interaction handlers to prevent unnecessary re-renders
  • Use React.memo on tooltip content components when they receive static props

Virtualization for large lists

  • Not applicable to tooltip component as it's a single-element UI pattern, not list-based
  • However, if tooltips are used in lists (e.g., table cells), ensure proper virtualization of list containers

Prefetching and preloading strategy

  • Preload tooltip content when user hovers near relevant elements (e.g., in a dashboard)
  • Implement prefetching for tooltips that are likely to be shown soon (e.g., in a navigation menu)
  • Use IntersectionObserver to detect when tooltips might be needed and preload content in advance
// Performance-optimized tooltip component with memoization strategy
import React, { useState, useCallback, useRef, useEffect } from 'react';

const Tooltip = React.memo(({ 
  content, 
  children,
  placement = 'top',
  delay = 300
}: {
  content: React.ReactNode;
  children: React.ReactNode;
  placement?: 'top' | 'bottom' | 'left' | 'right';
  delay?: number;
}) => {
  const [isVisible, setIsVisible] = useState(false);
  const [position, setPosition] = useState({ top: 0, left: 0 });
  const timeoutRef = useRef<NodeJS.Timeout | null>(null);
  const tooltipRef = useRef<HTMLDivElement>(null);
  const triggerRef = useRef<HTMLElement>(null);

  // Memoize positioning calculation to prevent unnecessary recalculations
  const calculatePosition = useCallback((triggerRect: DOMRect, tooltipRect: DOMRect) => {
    const positions = {
      top: { top: triggerRect.top - tooltipRect.height - 8, left: triggerRect.left + (triggerRect.width / 2) - (tooltipRect.width / 2) },
      bottom: { top: triggerRect.bottom + 8, left: triggerRect.left + (triggerRect.width / 2) - (tooltipRect.width / 2) },
      left: { top: triggerRect.top + (triggerRect.height / 2) - (tooltipRect.height / 2), left: triggerRect.left - tooltipRect.width - 8 },
      right: { top: triggerRect.top + (triggerRect.height / 2) - (tooltipRect.height / 2), left: triggerRect.right + 8 }
    };
    
    return positions[placement];
  }, [placement]);

  // Memoize event handlers to prevent re-creation on every render
  const handleMouseEnter = useCallback(() => {
    if (timeoutRef.current) clearTimeout(timeoutRef.current);
    
    timeoutRef.current = setTimeout(() => {
      setIsVisible(true);
    }, delay);
  }, [delay]);

  const handleMouseLeave = useCallback(() => {
    if (timeoutRef.current) clearTimeout(timeoutRef.current);
    setIsVisible(false);
  }, []);

  // Use effect to handle positioning updates efficiently
  useEffect(() => {
    if (isVisible && triggerRef.current && tooltipRef.current) {
      const triggerRect = triggerRef.current.getBoundingClientRect();
      const tooltipRect = tooltipRef.current.getBoundingClientRect();
      
      setPosition(calculatePosition(triggerRect, tooltipRect));
    }
  }, [isVisible, calculatePosition]);

  return (
    <div 
      ref={triggerRef}
      onMouseEnter={handleMouseEnter}
      onMouseLeave={handleMouseLeave}
      className="tooltip-trigger"
    >
      {children}
      {isVisible && (
        <div
          ref={tooltipRef}
          style={{
            position: 'absolute',
            top: `${position.top}px`,
            left: `${position.left}px`,
            transform: 'translateZ(0)', // Hardware acceleration
            willChange: 'transform', // Performance hint
            zIndex: 1000,
            pointerEvents: 'none'
          }}
          className="tooltip-content"
        >
          {content}
        </div>
      )}
    </div>
  );
});

export default Tooltip;

7. Error Handling & Resilience

Error Boundary Placement Strategy

Error boundaries should be strategically placed to prevent tooltip rendering failures from crashing the entire application. The primary placement is at the root of the tooltip component itself, with secondary boundaries around complex tooltip content that may contain user-provided content or external data.

graph TD
    A[App Root] --> B[Tooltip Component]
    B --> C[TooltipContent]
    C --> D[UserContent/ExternalData]
    B --> E[ErrorBoundary]
    E --> F[ErrorFallback]

Global Error Handler Design

A global error handler will capture unhandled errors from tooltip components and report them to monitoring systems. The handler will ensure that tooltip errors don't propagate to higher-level error boundaries and provide graceful fallback behavior.

Offline / Network Failure Handling

Tooltips should gracefully handle network failures when fetching dynamic content (e.g., user avatars, external data). The component should:

  • Display cached or default content when network is unavailable
  • Implement retry logic with exponential backoff for dynamic data
  • Show a "Network unavailable" indicator when appropriate

Graceful Degradation Patterns

When tooltips fail to render properly, the system should degrade gracefully:

  • Fallback to simple text-only tooltips
  • Disable animations or complex rendering
  • Show minimal content with clear error indicators
  • Maintain keyboard navigation accessibility even when tooltip fails

User Feedback on Errors

Tooltips should provide clear feedback when they fail to render:

  • Inline error messages for content errors
  • Toast notifications for critical tooltip failures
  • Fallback UI that maintains the component's purpose
  • Accessible error states with proper ARIA attributes
// Error boundary implementation for tooltip component
interface TooltipErrorState {
  hasError: boolean;
  error?: Error;
}

class TooltipErrorBoundary extends React.Component<{}, TooltipErrorState> {
  constructor(props: {}) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error: Error): TooltipErrorState {
    return { hasError: true, error };
  }

  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
    // Log error to monitoring service
    console.error('Tooltip component error:', error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      return (
        <div className="tooltip-fallback" role="alert">
          <span className="tooltip-error-icon">⚠️</span>
          <span>Tooltip content unavailable</span>
        </div>
      );
    }

    return this.props.children;
  }
}

Error Recovery Strategy

Tooltips should implement automatic recovery when transient errors occur:

  • Retry failed content fetches after a delay
  • Cache successful tooltip states for quick recovery
  • Implement circuit breaker pattern for persistent errors
  • Provide user-initiated refresh capability for failed tooltips

Fallback UI Design

When tooltip rendering fails, the fallback should:

  • Preserve the tooltip's positioning context
  • Show minimal content that conveys the same information
  • Maintain accessibility attributes and keyboard navigation
  • Provide clear indication that content is not fully loaded
// Fallback tooltip implementation
const TooltipFallback = ({ message }: { message?: string }) => (
  <div 
    className="tooltip tooltip-fallback"
    role="tooltip"
    aria-live="polite"
  >
    <div className="tooltip-content">
      {message || "Tooltip content unavailable"}
    </div>
  </div>
);

Monitoring Integration

All tooltip errors should be logged with:

  • Error type and stack trace
  • User context (if available)
  • Component state at time of error
  • Browser and environment information
  • Retry count and recovery status

Retry Logic Implementation

For network-dependent tooltip content:

const useTooltipRetry = (maxRetries = 3) => {
  const [retryCount, setRetryCount] = useState(0);
  
  const retry = useCallback(() => {
    if (retryCount < maxRetries) {
      setRetryCount(prev => prev + 1);
      // Implement exponential backoff
      setTimeout(() => {
        // Retry logic here
      }, Math.pow(2, retryCount) * 1000);
    }
  }, [retryCount, maxRetries]);

  return { retryCount, retry };
};

Performance Impact Considerations

Error handling should not impact tooltip performance:

  • Error boundaries should be lightweight
  • Fallback rendering should not block main thread
  • Error logging should be asynchronous when possible
  • Memory cleanup for failed tooltip instances

8. Testing Strategy

Unit Testing Approach

Component Tests (70% of test coverage)

  • Test all tooltip variants: top, bottom, left, right with proper positioning
  • Validate content rendering with text, JSX elements, and React components
  • Test delay, disabled, visible props behavior
  • Verify proper cleanup of event listeners and timers on unmount
  • Test onOpen and onClose callback execution with proper timing

Utility Tests (20% of test coverage)

  • Test positioning calculation logic with various element sizes and viewport dimensions
  • Validate getTooltipPosition function with edge case handling (viewport overflow, element centering)
  • Test useTooltip custom hook state transitions and side effects

Mocked Integration Tests (10% of test coverage)

  • Test tooltip behavior in complex layouts with scrollable containers
  • Validate proper positioning when parent elements have transform or position: fixed

Integration Testing Strategy

Cross-browser compatibility testing

  • Test tooltip positioning and rendering in Chrome, Firefox, Safari, Edge
  • Validate proper behavior with different browser zoom levels (100%, 125%, 150%)

Layout boundary testing

  • Test tooltip positioning when element is near viewport edges
  • Validate proper alignment when parent container has overflow: hidden
  • Test tooltip behavior inside scrollable containers with position: fixed

E2E Testing Scope

Cypress-based E2E coverage (50% of E2E tests)

  • User interaction flows: hover → tooltip appears → mouse moves to tooltip → tooltip stays open
  • Keyboard navigation support (Tab, Enter, Space) for accessible tooltips
  • Multi-tooltip scenarios: multiple tooltips on same page with proper isolation
  • Responsive behavior testing across mobile, tablet, desktop breakpoints

Critical user journeys:

  1. Hover over element with tooltip → verify tooltip appears within 200ms
  2. Move mouse to tooltip content → ensure tooltip remains open
  3. Move mouse away from element and tooltip → verify tooltip closes within 200ms
  4. Keyboard focus navigation through elements with tooltips
  5. Tooltip content updates when underlying data changes

Visual Regression Testing

Screenshot testing with Percy/Cypress

  • Capture tooltip positioning in various layouts (top, bottom, left, right)
  • Test with different content types (text only, rich text, React components)
  • Validate proper rendering in dark/light mode variants
  • Test responsive behavior across breakpoints (mobile, tablet, desktop)

Accessibility Testing Automation

axe-core integration with Jest

  • Test WCAG 2.1 AA compliance for all tooltip variants
  • Validate proper ARIA attribute usage (aria-describedby, role="tooltip")
  • Verify keyboard navigation accessibility
  • Test screen reader compatibility with NVDA, JAWS, VoiceOver

Test Data Strategy

Test fixtures and mock data

  • Create minimal test data for basic tooltip content (text, JSX)
  • Generate edge case scenarios: very long text, empty content, complex React components
  • Mock DOM element sizes and positions to simulate various viewport conditions
  • Use deterministic test IDs for reliable E2E selectors

Test coverage targets:

  • Component unit tests: 90% code coverage (line + branch)
  • Integration tests: 85% of key user flows covered
  • E2E tests: 70% of critical user journeys tested end-to-end
  • Accessibility tests: 100% WCAG AA compliance verified

9. Accessibility (A11y)

WCAG 2.1 AA compliance checklist for this feature

The tooltip component must meet the following WCAG 2.1 AA success criteria:

  • 1.1.1 Non-text Content: All tooltip content must be text-based or have appropriate text alternatives for non-text content
  • 1.4.13 Content on Hover or Focus: Tooltip content must be accessible when the trigger element receives focus (keyboard navigation) and not rely solely on hover
  • 1.4.3 Contrast Ratio: Tooltip text must have a contrast ratio of at least 4.5:1 against the background
  • 2.1.1 Keyboard: All tooltip functionality must be operable via keyboard navigation alone
  • 2.4.6 Headings and Labels: Tooltip trigger elements must have appropriate labels for screen readers
  • 3.1.1 Language: Tooltip content should respect language attributes when displaying translated text

Keyboard navigation support

The tooltip component must support full keyboard navigation:

  • Tab Navigation: Users can navigate to the trigger element using Tab key
  • Enter/Space Activation: Pressing Enter or Space while focused on the trigger activates the tooltip
  • Escape Dismissal: Pressing Escape key dismisses the active tooltip
  • Focus Management: When tooltip is shown, focus should not move away from the trigger element unless explicitly designed to do so

Focus management strategy

The tooltip component implements proper focus management:

  • Focus Trapping: When the tooltip is open, keyboard focus remains on the trigger element
  • Focus Restoration: When tooltip closes (via Escape or click outside), focus returns to the trigger element
  • Focusable Elements: Tooltip content should not contain focusable elements unless explicitly designed to be interactive

ARIA roles and attributes plan

The tooltip component uses appropriate ARIA attributes:

// Tooltip trigger element
<button 
  aria-describedby="tooltip-id"
  aria-expanded="false" // updated when tooltip opens
  aria-label="Tooltip description"
>
  {triggerContent}
</button>

// Tooltip element
<div 
  id="tooltip-id"
  role="tooltip"
  aria-hidden="true" // updated when tooltip is visible
  className="tooltip-content"
>
  {tooltipContent}
</div>

Screen reader compatibility

The tooltip component ensures screen reader compatibility:

  • Live Region: Tooltip content is announced when it becomes visible
  • Descriptive Labels: Trigger elements have descriptive labels that provide context for the tooltip content
  • Announcement Timing: Screen readers announce tooltip content immediately upon activation, not after a delay
  • No Conflicting Announcements: Tooltip content does not interfere with existing screen reader announcements

Additional accessibility considerations

  • Announcement Delay: Tooltip content should be announced immediately upon activation (no artificial delays)
  • Interactive Content: If tooltip contains interactive elements, they must be properly labeled and keyboard accessible
  • Dynamic Content Updates: When tooltip content changes dynamically, screen readers must be notified of the change
  • Color Contrast: Tooltip background and text must meet color contrast requirements for accessibility
  • Focus Indicators: The tooltip trigger element should maintain visible focus indicators when focused

Keyboard interaction flow

stateDiagram-v2
    [*] --> Idle
    Idle --> Showing : Focus + Enter/Space
    Showing --> Hiding : Escape
    Showing --> Idle : Click outside
    Hiding --> Idle : Animation complete

The tooltip component ensures that all keyboard interactions are fully accessible and meet WCAG 2.1 AA standards, providing equivalent functionality for users who rely on keyboard navigation or screen readers.

10. Security Considerations

XSS Prevention Strategy

The Tooltip component must prevent Cross-Site Scripting (XSS) attacks by sanitizing all content passed to it. Since tooltips can display user-generated content or dynamic text, raw HTML injection is a potential vulnerability.

// Secure tooltip content handling
const sanitizeTooltipContent = (content: string): string => {
  const div = document.createElement('div');
  div.textContent = content;
  return div.innerHTML;
};

// Alternative using DOMPurify for complex HTML
import DOMPurify from 'dompurify';

const sanitizeTooltipWithDOMPurify = (content: string): string => {
  return DOMPurify.sanitize(content, {
    ALLOWED_TAGS: ['b', 'i', 'em', 'strong'],
    ALLOWED_ATTR: [],
  });
};

CSRF Protection

While tooltips themselves don't typically make API calls, if they're used in contexts where dynamic content is fetched (e.g., tooltips that load from a backend), CSRF tokens should be handled at the application level through proper API client configuration.

Content Security Policy (CSP) Headers

The tooltip component should not introduce CSP violations. All inline styles and scripts must be avoided:

// ❌ Avoid - This would violate CSP
const tooltipStyle = `position: absolute; left: 0; top: 0;`;

// ✅ Safe - Use CSS classes instead
const tooltipStyle = 'tooltip-positioned';

Sensitive Data Handling in Frontend State

Tooltips should never expose sensitive data through their content or props. The component must validate that:

  1. No authentication tokens, session IDs, or PII are passed as tooltip content
  2. Tooltip content is filtered to exclude sensitive fields from dynamic data sources
// Example validation for tooltip content
const validateTooltipContent = (content: any): boolean => {
  if (typeof content === 'string') return true;
  if (typeof content === 'object' && content !== null) {
    // Check for sensitive keys in object
    const sensitiveKeys = ['token', 'sessionId', 'password', 'secret'];
    return !Object.keys(content).some(key => 
      sensitiveKeys.includes(key.toLowerCase())
    );
  }
  return false;
};

Input Sanitization Approach

All tooltip content should be sanitized before rendering:

// Sanitize tooltip content in component
const Tooltip: React.FC<TooltipProps> = ({ 
  content, 
  children,
  ...rest 
}) => {
  const sanitizedContent = useMemo(() => {
    if (typeof content === 'string') {
      return sanitizeTooltipContent(content);
    }
    // For complex content, use DOMPurify or similar
    return sanitizeTooltipWithDOMPurify(JSON.stringify(content));
  }, [content]);

  return (
    <div 
      {...rest}
      dangerouslySetInnerHTML={{ __html: sanitizedContent }}
    >
      {children}
    </div>
  );
};

Security Headers Considerations

The tooltip component should not require any special security headers but must ensure that any dynamically generated content is properly escaped to prevent XSS in environments with strict CSP policies.

Risk Mitigation

  1. Content Validation: All tooltip content must be validated and sanitized before rendering
  2. Secure Rendering: Never use dangerouslySetInnerHTML without proper sanitization
  3. Input Filtering: Implement strict input filtering for dynamic tooltip content
  4. CSP Compliance: Ensure no inline styles or scripts are generated by the component
  5. Audit Trail: Log any tooltip content that might contain sensitive data for security review

The component should be designed with security as a primary concern, treating all user-provided or dynamic content as potentially malicious and applying appropriate sanitization techniques.

11. Internationalization (i18n)

Translation Key Structure and Naming Convention

Translation keys for tooltips will follow a consistent naming pattern to ensure maintainability and scalability:

tooltip.{componentName}.{property}

For example:

  • tooltip.button.save
  • tooltip.input.email
  • tooltip.card.userProfile

Keys will be organized in nested JSON files per locale:

src/locales/en/tooltip.json
src/locales/fr/tooltip.json
src/locales/es/tooltip.json

RTL Support Considerations

RTL support will be handled through CSS direction properties and layout adjustments:

  1. Tooltip container will use direction: rtl when locale is RTL
  2. Arrow positioning will be inverted for RTL layouts (left arrow becomes right)
  3. Text alignment will be set to right in RTL contexts
  4. Popover positioning logic will account for RTL by adjusting horizontal offsets

Date, Number, Currency Formatting Strategy

Tooltips that display dynamic content will use the Intl API for proper formatting:

// Example of date formatting in tooltip content
const formatDate = (date: Date, locale: string) => {
  return new Intl.DateTimeFormat(locale, {
    year: 'numeric',
    month: 'long',
    day: 'numeric'
  }).format(date);
};

// Example of number formatting in tooltip content
const formatNumber = (value: number, locale: string) => {
  return new Intl.NumberFormat(locale, {
    style: 'currency',
    currency: 'USD'
  }).format(value);
};

Locale Detection and Switching Strategy

  1. Automatic Detection:

    • Use navigator.language as primary source
    • Fallback to en-US if no supported language detected
    • Store preferred locale in localStorage for persistence
  2. Manual Switching:

    • Provide a language selector component in the UI
    • Update i18next instance with new locale
    • Trigger re-render of all translated components
  3. Server-side Considerations:

    • Pass locale in HTTP headers or query parameters for SSR
    • Use Accept-Language header for automatic detection
// Example locale switching hook
const useLocaleSwitcher = () => {
  const [currentLocale, setCurrentLocale] = useState<string>('en-US');
  
  const switchLocale = useCallback((newLocale: string) => {
    i18next.changeLanguage(newLocale);
    setCurrentLocale(newLocale);
    localStorage.setItem('preferredLocale', newLocale);
  }, []);

  useEffect(() => {
    const savedLocale = localStorage.getItem('preferredLocale');
    if (savedLocale && i18next.language !== savedLocale) {
      i18next.changeLanguage(savedLocale);
      setCurrentLocale(savedLocale);
    }
  }, []);

  return { currentLocale, switchLocale };
};

12. Monitoring & Observability

Real User Monitoring (RUM) metrics to track

The tooltip component should capture key performance indicators for user experience monitoring:

  • Interaction latency: Time from user hover/focus to tooltip appearance (target < 100ms)
  • Tooltip visibility duration: How long tooltips remain visible to users (target > 200ms for readability)
  • Tooltip activation rate: Percentage of tooltip triggers that result in visible tooltips (target > 95% success rate)
  • Scroll performance impact: Tooltip behavior during scroll operations (max 100ms additional render time)
  • Memory usage: Tooltip instances and DOM nodes created per user session (target < 50ms heap increase)

Error tracking integration

Integrate with Sentry or similar error monitoring tools to capture:

  • Tooltip rendering failures (e.g., DOM element not found, positioning errors)
  • State transition errors in tooltip lifecycle management
  • Accessibility violation reports (focus trap issues, ARIA attribute problems)
  • Performance degradation alerts when tooltip interactions exceed thresholds

Custom events and user journey tracking

Implement custom event tracking for:

// Event tracking for tooltip interactions
const trackTooltipEvent = (event: 'tooltip_open' | 'tooltip_close' | 'tooltip_hover' | 'tooltip_focus') => {
  // Send to analytics service (e.g., Mixpanel, Google Analytics)
  if (window.mixpanel) {
    window.mixpanel.track(`tooltip_${event}`, {
      timestamp: Date.now(),
      tooltipId: 'unique-tooltip-id',
      userAction: event,
      viewportPosition: { x: window.scrollX, y: window.scrollY }
    });
  }
};

Performance monitoring and regression detection

  • LCP (Largest Contentful Paint): Ensure tooltip content doesn't block main thread rendering
  • FID (First Input Delay): Monitor that tooltip interactions don't increase input delay beyond 100ms
  • CLS (Cumulative Layout Shift): Track that tooltip positioning doesn't cause layout shifts > 0.1
  • Bundle size impact: Monitor tooltip component bundle size to ensure it stays under 2KB (minified + gzipped)
  • Memory leak detection: Monitor tooltip instance count over time to detect potential memory leaks

Performance monitoring will be implemented using:

  • Web Vitals SDK for real-time performance metrics
  • Custom performance observer to track tooltip-specific rendering behavior
  • Automated regression testing in CI pipeline to catch performance degradation

Observability tooling integration

The tooltip component will integrate with:

  • Sentry Performance Monitoring: For error tracking and performance insights
  • DataDog APM: For end-to-end transaction tracing including tooltip interactions
  • Lighthouse CI: For automated accessibility and performance audits
  • Chrome DevTools Profiler: For detailed rendering and memory usage analysis

The component will include optional telemetry configuration that allows teams to:

  1. Enable/disable specific monitoring points
  2. Set custom thresholds for performance alerts
  3. Configure sampling rates for high-volume tooltip interactions
  4. Send anonymized usage analytics to product teams for feature adoption insights
// Example telemetry configuration interface
interface TooltipTelemetryConfig {
  enabled: boolean;
  performanceMetrics: {
    thresholdMs: number;
    sampleRate: number;
  };
  errorReporting: {
    enabled: boolean;
    reportToSentry?: boolean;
  };
  userJourneyTracking: {
    enabled: boolean;
    events: string[];
  };
}

13. File & Folder Structure

Proposed Directory Structure

src/
├── components/
│   ├── Tooltip/
│   │   ├── index.ts
│   │   ├── Tooltip.tsx
│   │   ├── Tooltip.types.ts
│   │   ├── Tooltip.module.css
│   │   └── __tests__/
│   │       └── Tooltip.test.tsx
├── features/
│   └── tooltip/
│       ├── index.ts
│       └── hooks/
│           └── useTooltip.ts
└── types/
    └── tooltip.ts

Module Boundaries and Import Rules

  • Tooltip component is self-contained with no cross-feature dependencies
  • All internal imports use relative paths (../)
  • Shared utilities and types are imported from src/types/ or src/utils/
  • No circular dependencies between feature modules

Shared vs Feature-Specific Code Separation

Shared code:

  • Common types (TooltipPosition, TooltipTrigger) in src/types/tooltip.ts
  • CSS modules for consistent styling
  • Reusable utility functions (e.g., getBoundingClientRect)

Feature-specific code:

  • Component implementation and tests
  • Custom hooks for tooltip behavior
  • Feature-level exports and barrel files

This structure follows a feature-based organization that scales well for teams working on multiple UI components, with clear separation between shared and feature-specific concerns. The directory structure makes it easy to locate and test the tooltip component in isolation while maintaining clean import paths.

14. Dependencies & Third-Party Libraries

Key Libraries Selected with Justification

1. React (v18+)

  • Justification: Core library for building UI components with hooks, context, and proper component lifecycle management. Essential for any modern React application.
  • Bundle Impact: ~100KB (minified + gzip)
  • Alternatives Considered: Preact - smaller bundle but less ecosystem support and more complex migration path

2. TypeScript (v5+)

  • Justification: Provides type safety that prevents runtime errors and improves developer experience with better IDE support. Critical for maintaining large-scale frontend applications.
  • Bundle Impact: None (compile-time only)
  • Alternatives Considered: Flow - more complex setup, less adoption in the React ecosystem

3. Styled Components / Tailwind CSS

  • Justification: For styling, Tailwind CSS is chosen for its utility-first approach that enables rapid development and consistent design system implementation. It provides good performance with minimal runtime overhead.
  • Bundle Impact: ~10KB (Tailwind) vs ~25KB (Styled Components)
  • Alternatives Considered: CSS Modules - more verbose, less maintainable for large teams

4. Framer Motion (v10+)

  • Justification: Provides smooth animations and transitions for tooltip show/hide states. Enables declarative animation control with minimal code overhead.
  • Bundle Impact: ~20KB (minified + gzip)
  • Alternatives Considered: CSS transitions - more verbose, less flexible for complex animations

5. React Portal (built-in)

  • Justification: Required to render tooltips outside of their parent DOM context to prevent z-index and overflow issues.
  • Bundle Impact: None (built-in)
  • Alternatives Considered: Manual DOM manipulation - more error-prone and harder to maintain

6. @floating-ui/react (v0.13+)

  • Justification: Provides robust positioning logic for tooltips that handles edge cases like viewport boundaries, collision detection, and responsive positioning.
  • Bundle Impact: ~15KB (minified + gzip)
  • Alternatives Considered: Popper.js - more complex API, less active maintenance

Alternatives Considered and Rejected

1. React Tooltip (v4+)

  • Rejected: While feature-rich, it's overly heavy (~50KB) and introduces unnecessary complexity for a simple tooltip component. The design system integration would require extensive customization.

2. React Bootstrap Tooltip

  • Rejected: Tied to Bootstrap's design system and CSS framework, which would force unwanted styling dependencies. Not suitable for custom design systems.

3. CSS-only tooltip libraries

  • Rejected: Lack of proper positioning logic and accessibility features, requiring extensive custom JavaScript to handle edge cases.

Licensing and Bundle Size Impact Assessment

Bundle Size Budget:

  • Core dependencies (React + TypeScript) ~120KB
  • Styling and animation libraries ~35KB
  • Positioning library ~15KB
  • Total estimated runtime bundle size: ~170KB

License Compliance: All selected libraries are MIT licensed with no restrictions on commercial usage. No proprietary dependencies that would require enterprise licensing.

Tree-shaking Strategy: All libraries support tree-shaking for optimal bundle size. Only required components are imported to minimize final bundle size.

Performance Impact: The chosen libraries provide minimal runtime overhead while offering essential functionality. Framer Motion and Floating UI are optimized for performance with proper memoization and minimal DOM operations.

Maintenance Considerations: All libraries are actively maintained with regular updates. Floating UI and Framer Motion have strong community support and clear migration paths for version upgrades.

15. Open Questions, Risks & Trade-offs

Decisions that need product/engineering alignment

  • Tooltip placement strategy: Should tooltips always appear above/below elements or support dynamic positioning? This affects the complexity of the positioning engine and whether we should use a library like Popper.js vs. custom implementation.
  • Trigger behavior: Should tooltips activate on hover, click, or both? This impacts the component's API surface and accessibility requirements.
  • Animation timing: What are the specific animation durations and easing functions? These affect perceived performance and user experience consistency.
  • Content composition: Should tooltips support rich content (HTML, React components) or be limited to plain text? This affects the component's flexibility and security considerations.

Known trade-offs made in this design

  • Performance vs. Feature Richness: Prioritized lightweight implementation over complex features like auto-scroll adjustment or dynamic content rendering to maintain optimal bundle size and rendering performance.
  • Accessibility vs. Simplicity: Chose a minimal ARIA implementation to reduce complexity, accepting that more advanced keyboard navigation patterns might be lost in favor of basic screen reader support.
  • Custom vs. Third-party Implementation: Decided against using a mature tooltip library like react-tooltip to maintain full control over the component's behavior and avoid dependency bloat, but this increases implementation complexity.
  • Static vs. Dynamic Positioning: Used a simpler static positioning model rather than complex adaptive positioning to reduce code surface area and potential edge case bugs.

Risks and mitigations

  • Positioning overflow: Risk that tooltips may render outside viewport boundaries. Mitigation: Implement boundary detection and automatic repositioning logic.
  • Accessibility regressions: Risk of breaking keyboard navigation or screen reader support. Mitigation: Comprehensive accessibility testing and integration with existing A11y testing frameworks.
  • Performance impact: Risk of layout thrashing or excessive re-renders in high-frequency usage. Mitigation: Thorough performance testing with React DevTools Profiler and implementing proper memoization.
  • Cross-browser compatibility: Risk of inconsistent rendering across browsers. Mitigation: Comprehensive browser testing and graceful degradation strategies for older browsers.

Technical debt acknowledged

  • Limited content flexibility: The current design restricts tooltip content to simple text, which may require refactoring if rich content support is needed in the future.
  • Basic positioning logic: The current placement algorithm only handles top/bottom/left/right positions, requiring future enhancement for more complex positioning strategies.
  • No built-in theming support: The component uses hardcoded styles that may not integrate well with design systems, requiring future abstraction for theme customization.
  • Limited animation control: Animation timing is hardcoded, which may not accommodate all design system requirements without refactoring.

Was this article helpful?

Topics in this article

More from Frontend LLD

  • Frontend LLD

    Comprehensive Toast Notification System Design

    Detailed low-level design for a scalable, accessible toast notification system with queue management and customizable behavior patterns

  • Frontend LLD

    Calendar Component Design System

    Comprehensive low-level design for a scalable, accessible calendar component with recurring events and timezone support

Stay in the Loop

Get curated DSA patterns, coding problems, frontend and backend LLD/HLD, and AI/ML System Design insights delivered straight to your inbox. No spam, just focused interview and engineering content.