CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-tabs

An accessible and easy tab component for ReactJS with full keyboard navigation and ARIA support

Pending
Overview
Eval results
Files

accessibility.mddocs/

Accessibility Features

Full accessibility support with ARIA compliance, keyboard navigation, and focus management for screen readers and assistive technologies.

Core Imports

import { Tabs, TabList, Tab, TabPanel } from "react-tabs";

Capabilities

ARIA Compliance

Complete ARIA implementation following WAI-ARIA authoring practices for tab interfaces.

/**
 * ARIA attributes automatically applied:
 * - Tabs: container with data-rttabs identifier
 * - TabList: role="tablist" 
 * - Tab: role="tab", aria-selected, aria-disabled, aria-controls
 * - TabPanel: role="tabpanel", aria-labelledby
 */
interface ARIAAttributes {
  /** Applied to TabList component */
  'role': 'tablist';
  /** Applied to Tab components */
  'role': 'tab';
  'aria-selected': 'true' | 'false';
  'aria-disabled': 'true' | 'false';
  'aria-controls': string; // References panel ID
  /** Applied to TabPanel components */
  'role': 'tabpanel';
  'aria-labelledby': string; // References tab ID
}

ARIA Structure Example:

<!-- Generated HTML structure with ARIA attributes -->
<div class="react-tabs" data-rttabs>
  <ul role="tablist" class="react-tabs__tab-list">
    <li role="tab" 
        id="tab0" 
        aria-selected="true" 
        aria-disabled="false"
        aria-controls="panel0"
        tabindex="0">
      Tab 1
    </li>
    <li role="tab" 
        id="tab1" 
        aria-selected="false" 
        aria-disabled="false"
        aria-controls="panel1"
        tabindex="-1">
      Tab 2
    </li>
  </ul>
  <div role="tabpanel" 
       id="panel0" 
       aria-labelledby="tab0">
    Panel 1 Content
  </div>
  <div role="tabpanel" 
       id="panel1" 
       aria-labelledby="tab1">
    Panel 2 Content
  </div>
</div>

Keyboard Navigation

Full keyboard navigation support following standard tab interface patterns.

interface KeyboardNavigationProps {
  /** Disable up/down arrow key navigation */
  disableUpDownKeys?: boolean; // Default: false
  /** Disable left/right arrow key navigation */
  disableLeftRightKeys?: boolean; // Default: false
  /** Text direction for navigation */
  direction?: 'rtl' | 'ltr'; // Default: 'ltr'
}

/**
 * Supported keyboard interactions:
 * - Arrow Left/Up: Previous tab (respects RTL)
 * - Arrow Right/Down: Next tab (respects RTL)  
 * - Home: First tab
 * - End: Last tab
 * - Enter/Space: Activate focused tab
 * - Tab: Move to tab panel content
 */
interface KeyboardEvents {
  ArrowLeft: () => void;
  ArrowRight: () => void;
  ArrowUp: () => void;
  ArrowDown: () => void;
  Home: () => void;
  End: () => void;
  Enter: () => void;
  Space: () => void;
}

Keyboard Navigation Examples:

// Standard keyboard navigation
<Tabs>
  <TabList>
    <Tab>Home</Tab>
    <Tab>About</Tab>
    <Tab>Contact</Tab>
  </TabList>
  <TabPanel>Home Content</TabPanel>
  <TabPanel>About Content</TabPanel>
  <TabPanel>Contact Content</TabPanel>
</Tabs>

// Disable vertical arrow keys (horizontal tabs only)
<Tabs disableUpDownKeys={true}>
  <TabList>
    <Tab>Tab 1</Tab>
    <Tab>Tab 2</Tab>
  </TabList>
  <TabPanel>Content 1</TabPanel>
  <TabPanel>Content 2</TabPanel>
</Tabs>

// RTL support with reversed navigation
<Tabs direction="rtl">
  <TabList>
    <Tab>علامة التبويب 1</Tab>
    <Tab>علامة التبويب 2</Tab>
  </TabList>
  <TabPanel>المحتوى 1</TabPanel>
  <TabPanel>المحتوى 2</TabPanel>
</Tabs>

// Disable all arrow key navigation
<Tabs disableUpDownKeys={true} disableLeftRightKeys={true}>
  <TabList>
    <Tab>Tab 1</Tab>
    <Tab>Tab 2</Tab>
  </TabList>
  <TabPanel>Content 1</TabPanel>
  <TabPanel>Content 2</TabPanel>
</Tabs>

Focus Management

Comprehensive focus management for optimal keyboard and screen reader experience.

interface FocusManagementProps {
  /** Focus the selected tab on initial render */
  defaultFocus?: boolean; // Default: false
  /** Focus tab when clicked */
  focusTabOnClick?: boolean; // Default: true
  /** Window environment for SSR compatibility */
  environment?: Window;
}

/**
 * Focus behavior:
 * - Only selected tab is focusable (tabindex="0")
 * - Other tabs have tabindex="-1" 
 * - Focus moves with selection via keyboard
 * - Click focus can be disabled
 */
interface FocusBehavior {
  selectedTabIndex: 0;
  otherTabsIndex: -1;
  movesFocusWithSelection: boolean;
  respectsReducedMotion: boolean;
}

Focus Management Examples:

// Auto-focus selected tab on mount
<Tabs defaultFocus={true}>
  <TabList>
    <Tab>Tab 1</Tab>
    <Tab>Tab 2</Tab>
  </TabList>
  <TabPanel>Content 1</TabPanel>
  <TabPanel>Content 2</TabPanel>
</Tabs>

// Disable focus on click (for custom focus handling)
<Tabs focusTabOnClick={false}>
  <TabList>
    <Tab>Tab 1</Tab>
    <Tab>Tab 2</Tab>
  </TabList>
  <TabPanel>Content 1</TabPanel>
  <TabPanel>Content 2</TabPanel>
</Tabs>

// Custom focus handling with refs
function CustomFocusExample() {
  const tabsRef = useRef();
  
  const focusTab = (index) => {
    const tab = tabsRef.current.querySelector(`[aria-controls="panel${index}"]`);
    if (tab) tab.focus();
  };

  return (
    <Tabs 
      domRef={(node) => tabsRef.current = node}
      focusTabOnClick={false}
    >
      <TabList>
        <Tab>Tab 1</Tab>
        <Tab>Tab 2</Tab>
      </TabList>
      <TabPanel>Content 1</TabPanel>
      <TabPanel>Content 2</TabPanel>
    </Tabs>
  );
}

Disabled Tab Support

Accessible disabled tab implementation with proper ARIA attributes and navigation skipping.

interface DisabledTabProps {
  /** Whether the tab is disabled */
  disabled?: boolean; // Default: false
  /** CSS class for disabled tabs */
  disabledClassName?: string; // Default: "react-tabs__tab--disabled"
}

/**
 * Disabled tab behavior:
 * - aria-disabled="true" attribute
 * - Skipped during keyboard navigation
 * - Visual disabled styling applied
 * - Click events prevented
 */
interface DisabledTabBehavior {
  'aria-disabled': 'true';
  clickable: false;
  keyboardNavigable: false;
  screenReaderAccessible: true;
}

Disabled Tab Examples:

// Individual disabled tabs
<TabList>
  <Tab>Available</Tab>
  <Tab disabled>Coming Soon</Tab>
  <Tab>Available</Tab>
</TabList>

// Conditional disabled state
<TabList>
  <Tab>Public</Tab>
  <Tab disabled={!user.isAdmin}>Admin Only</Tab>
  <Tab disabled={!user.isPremium}>Premium Feature</Tab>
</TabList>

// Custom disabled styling
<Tabs disabledTabClassName="custom-disabled">
  <TabList>
    <Tab>Enabled</Tab>
    <Tab disabled disabledClassName="special-disabled">
      Special Disabled
    </Tab>
  </TabList>
  <TabPanel>Enabled Content</TabPanel>
  <TabPanel>This won't show</TabPanel>
</Tabs>

Screen Reader Support

Optimized experience for screen readers with proper announcements and navigation.

/**
 * Screen reader optimizations:
 * - Proper role attributes for semantic meaning
 * - Live region updates for dynamic content
 * - Descriptive labels and associations
 * - Tab count and position announcements
 */
interface ScreenReaderFeatures {
  semanticRoles: boolean;
  liveRegionSupport: boolean;
  labelAssociations: boolean;
  positionAnnouncements: boolean;
}

Screen Reader Enhancement Examples:

// Enhanced labeling for screen readers
<Tabs>
  <TabList aria-label="Main navigation tabs">
    <Tab aria-describedby="tab1-desc">
      Dashboard
      <span id="tab1-desc" className="sr-only">
        Overview of your account
      </span>
    </Tab>
    <Tab aria-describedby="tab2-desc">
      Settings
      <span id="tab2-desc" className="sr-only">
        Account preferences and configuration
      </span>
    </Tab>
  </TabList>
  <TabPanel>
    <h2>Dashboard</h2>
    <p>Welcome to your account dashboard.</p>
  </TabPanel>
  <TabPanel>
    <h2>Settings</h2>
    <p>Manage your account settings.</p>
  </TabPanel>
</Tabs>

// Dynamic content with live regions
function DynamicTabsExample() {
  const [tabData, setTabData] = useState([]);
  
  return (
    <Tabs>
      <TabList>
        {tabData.map((tab, index) => (
          <Tab key={tab.id}>
            {tab.title}
            {tab.hasUpdates && (
              <span aria-live="polite" className="sr-only">
                New updates available
              </span>
            )}
          </Tab>
        ))}
      </TabList>
      {tabData.map((tab, index) => (
        <TabPanel key={tab.id}>
          <div aria-live="polite">
            {tab.content}
          </div>
        </TabPanel>
      ))}
    </Tabs>
  );
}

RTL Support

Right-to-left text direction support with appropriate keyboard navigation reversal.

interface RTLProps {
  /** Text direction for RTL languages */
  direction?: 'rtl' | 'ltr'; // Default: 'ltr'
}

/**
 * RTL behavior:
 * - Reverses left/right arrow key meanings
 * - Maintains logical tab order
 * - Compatible with CSS dir attribute
 * - Preserves up/down navigation
 */
interface RTLBehavior {
  arrowLeftAction: 'next' | 'previous'; // Depends on direction
  arrowRightAction: 'next' | 'previous'; // Depends on direction
  visualOrder: 'ltr' | 'rtl';
  logicalOrder: 'consistent';
}

RTL Support Examples:

// Arabic/Hebrew interface
<div dir="rtl">
  <Tabs direction="rtl">
    <TabList>
      <Tab>الرئيسية</Tab>
      <Tab>حول</Tab>
      <Tab>اتصل</Tab>
    </TabList>
    <TabPanel>محتوى الرئيسية</TabPanel>
    <TabPanel>محتوى حول</TabPanel>
    <TabPanel>محتوى اتصل</TabPanel>
  </Tabs>
</div>

// Dynamic direction based on content
function MultiLanguageTabs({ language }) {
  const isRTL = ['ar', 'he', 'fa'].includes(language);
  
  return (
    <div dir={isRTL ? 'rtl' : 'ltr'}>
      <Tabs direction={isRTL ? 'rtl' : 'ltr'}>
        <TabList>
          <Tab>{translations[language].home}</Tab>
          <Tab>{translations[language].about}</Tab>
        </TabList>
        <TabPanel>{translations[language].homeContent}</TabPanel>
        <TabPanel>{translations[language].aboutContent}</TabPanel>
      </Tabs>
    </div>
  );
}

Environment Support

Server-side rendering and custom environment support for accessibility features.

interface EnvironmentProps {
  /** Window environment for SSR compatibility */
  environment?: Window;
}

/**
 * Environment handling for different runtime contexts:
 * - SSR compatibility with no DOM access during server rendering
 * - Custom window object support for iframe/popup contexts  
 * - IE compatibility workarounds for document.activeElement access bugs
 * - Focus detection in non-standard environments
 */
interface EnvironmentSupport {
  SSRCompatible: boolean;
  customWindowSupport: boolean;
  activeElementDetection: boolean;  
  IECompatibility: boolean;
}

Environment Support Examples:

// Custom environment (e.g., iframe or popup window)
const iframeWindow = document.getElementById('myFrame').contentWindow;
<Tabs environment={iframeWindow}>
  <TabList>
    <Tab>Tab 1</Tab>
    <Tab>Tab 2</Tab>
  </TabList>
  <TabPanel>Content 1</TabPanel>
  <TabPanel>Content 2</TabPanel>
</Tabs>

// IE compatibility workaround (fixes activeElement access in iframes)
const safeEnvironment = (() => {
  try {
    return window.document.activeElement ? window : undefined;
  } catch (e) {
    // IE throws error accessing activeElement in iframes
    return undefined;
  }
})();

<Tabs environment={safeEnvironment}>
  {/* components */}
</Tabs>

// SSR-safe implementation
function SSRSafeTabs() {
  const [mounted, setMounted] = useState(false);
  
  useEffect(() => {
    setMounted(true);
  }, []);

  return (
    <Tabs 
      environment={mounted ? window : undefined}
      defaultFocus={false} // Avoid auto-focus on SSR
    >
      <TabList>
        <Tab>Tab 1</Tab>
        <Tab>Tab 2</Tab>
      </TabList>
      <TabPanel>Content 1</TabPanel>
      <TabPanel>Content 2</TabPanel>
    </Tabs>
  );
}

Install with Tessl CLI

npx tessl i tessl/npm-react-tabs

docs

accessibility.md

components.md

index.md

state-management.md

styling.md

tile.json