Skip to main content

Overview

Mintlify Components is built with accessibility in mind, following WAI-ARIA best practices. All interactive components include proper ARIA attributes, keyboard navigation support, and focus management.
The library uses @base-ui/react and @headlessui/react for accessible component primitives.

ARIA Attributes

Components use ARIA attributes to provide context for assistive technologies.

Accordion Component

From packages/components/src/components/accordion/accordion-cover.tsx:24-31:
<summary
  aria-controls={`${id}-accordion-children`}
  aria-expanded={open}
  className="..."
  id={`${id}-label`}
>
  {/* Accordion trigger content */}
</summary>

<div
  aria-labelledby={`${id}-label`}
  id={`${id}-accordion-children`}
  role="region"
>
  {children}
</div>
ARIA attributes used:
  • aria-controls: Links the trigger to the content it controls
  • aria-expanded: Indicates whether the accordion is open or closed
  • aria-labelledby: Associates the content region with its label
  • role="region": Identifies the content as a landmark region

Tabs Component

From packages/components/src/components/tabs/tabs.tsx:165-199:
<ul
  role="tablist"
  aria-label={ariaLabel ?? "Tabs"}
>
  <li
    role="tab"
    aria-selected={isActive}
    aria-controls={`panel-${tabIds[i]}`}
    tabIndex={isActive ? 0 : -1}
  >
    {title}
  </li>
</ul>

<div
  role="tabpanel"
  aria-labelledby={tabIds[i]}
  aria-hidden={!isActive}
  tabIndex={isActive ? 0 : -1}
>
  {content}
</div>
ARIA attributes used:
  • role="tablist": Identifies the tab container
  • role="tab": Identifies each tab trigger
  • role="tabpanel": Identifies each content panel
  • aria-selected: Indicates the active tab
  • aria-controls: Links tabs to their panels
  • aria-labelledby: Associates panels with their tabs
  • aria-hidden: Hides inactive panels from screen readers

    Tooltip Component

    From packages/components/src/components/tooltip/tooltip.tsx:59-79:
    <TooltipBaseUI.Trigger
      aria-label={ariaLabel}
      onClick={handleClick}
    >
      {children}
    </TooltipBaseUI.Trigger>
    
    The component automatically generates aria-label based on content:
    const ariaLabel = useMemo(() => {
      if (isInteractive) {
        return undefined; // Interactive elements have their own labels
      }
      
      if (title && description) {
        return `${title}: ${description}`;
      }
      
      return title || description;
    }, [title, description, isInteractive]);
    

    Card Component

    From packages/components/src/components/card/card.tsx:192-193:
    <div
      aria-hidden="true"
      className="absolute top-5 right-5"
    >
      <ArrowUpRight className="size-4" />
    </div>
    
    ARIA pattern:
    • Decorative icons use aria-hidden="true" to hide from screen readers
    • Links and buttons have descriptive text for context

    Color Component

    From packages/components/src/components/color/color.tsx:127-129:
    <button
      aria-label={name || `Color ${currentColor}`}
      aria-pressed={state === "copied"}
      aria-roledescription={state === "copied" ? "Copied" : "Copy"}
    >
      {/* Color swatch */}
    </button>
    
    ARIA attributes used:
    • aria-label: Descriptive label for the color button
    • aria-pressed: Indicates copy state
    • aria-roledescription: Describes the button’s purpose

    Keyboard Navigation

    All interactive components support keyboard navigation.

    Tabs Keyboard Support

    From packages/components/src/components/tabs/tabs.tsx:118-154:
    const handleKeyDown = (e: KeyboardEvent, currentIndex: number) => {
      if (e.key === "ArrowLeft" || e.key === "ArrowRight") {
        e.preventDefault();
        const newIndex = e.key === "ArrowLeft"
          ? (currentIndex - 1 + tabCount) % tabCount
          : (currentIndex + 1) % tabCount;
        handleTabClick(newIndex);
        tabRefs.current[newIndex]?.focus();
      } else if (e.key === "Enter" || e.key === " ") {
        e.preventDefault();
        handleTabClick(currentIndex);
      } else if (e.key === "Home") {
        e.preventDefault();
        handleTabClick(0);
        tabRefs.current[0]?.focus();
      } else if (e.key === "End") {
        e.preventDefault();
        handleTabClick(lastIndex);
        tabRefs.current[lastIndex]?.focus();
      }
    };
    
    Keyboard shortcuts:
    • ArrowLeft: Previous tab
    • ArrowRight: Next tab
    • Home: First tab
    • End: Last tab
    • Enter or Space: Activate tab
    • Tab: Move focus out of tab list

    Accordion Keyboard Support

    Accordions use the native <details> element:
    <details
      open={open}
      onToggle={(e) => handleToggle(e)}
    >
      <summary>{title}</summary>
      {children}
    </details>
    
    Keyboard shortcuts:
    • Enter or Space: Toggle accordion
    • Tab: Navigate between accordions
    Using the native <details> element provides built-in keyboard support and proper semantics.

    Steps Keyboard Support

    From packages/components/src/components/steps/steps.tsx:179-180:
    <a
      aria-label="Navigate to header"
      href={`#${encodeURIComponent(id)}`}
      onClick={(e) => {
        e.preventDefault();
        copyAnchorLink();
      }}
    >
      <LinkIcon />
    </a>
    
    Keyboard shortcuts:
    • Tab: Navigate between step anchor links
    • Enter: Copy anchor link and update URL hash

    Focus Management

    Components implement proper focus management for keyboard users.

    Focus Styles

    From packages/components/src/components/card/card.tsx:168:
    "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary"
    
    From packages/components/src/components/tooltip/tooltip.tsx:82-84:
    "rounded-sm focus-visible:outline-none focus-visible:ring-2 "
    "focus-visible:ring-primary focus-visible:ring-offset-2"
    
    Focus pattern:
    • Remove default outline with outline-none
    • Add custom focus ring with ring-2
    • Use theme primary color for consistency
    • Add ring offset for better visibility
    Always use focus-visible instead of focus to avoid showing focus rings on mouse clicks.

    Tab Index Management

    From packages/components/src/components/tabs/tabs.tsx:199:
    tabIndex={isActive ? 0 : -1}
    
    Pattern:
    • Active tab: tabIndex={0} (keyboard focusable)
    • Inactive tabs: tabIndex={-1} (not in tab order)
    • Content panels: Match their tab’s active state

    Focus Trapping

    Tooltips implement focus behavior based on device capabilities:
    const hasHover = useHasHover();
    
    const handleOpenChange = (nextOpen: boolean, eventDetails) => {
      if (hasHover) {
        setOpen(nextOpen);
      } else if (reason === "escape-key" || reason === "outside-press") {
        setOpen(false);
      }
    };
    

    Semantic HTML

    Components use semantic HTML elements when possible.

    List Semantics

    From packages/components/src/components/steps/steps.tsx:274-278:
    <div role="list">
      <div role="listitem">
        {/* Step content */}
      </div>
    </div>
    

    Landmark Roles

    <div role="region" aria-labelledby="accordion-label">
      {/* Accordion content */}
    </div>
    
    While divs with ARIA roles are used for styling flexibility, the library aims to use semantic HTML wherever possible.

    Screen Reader Support

    Callout Variants

    From packages/components/src/components/callout/callout.tsx:36-78:
    const variantConfig = {
      info: {
        icon: InfoIcon,
        defaultAriaLabel: "Info",
      },
      warning: {
        icon: WarningIcon,
        defaultAriaLabel: "Warning",
      },
      danger: {
        icon: DangerIcon,
        defaultAriaLabel: "Danger",
      },
    };
    
    Each callout variant has a default aria-label for screen readers:
    <Callout variant="warning">
      Important message
    </Callout>
    // Announces: "Warning: Important message"
    

    Hidden Content

    // Decorative icon hidden from screen readers
    <ChevronRightIcon aria-hidden="true" />
    
    // Content hidden from screen readers
    <div aria-hidden={!isActive}>
      {inactiveContent}
    </div>
    

    Color Contrast

    All components maintain WCAG AA contrast ratios.

    Text Contrast

    From packages/components/src/components/badge/badge.tsx:29-48:
    colorVariants: {
      blue: "[--color-text:#133A9A] dark:[--color-text:#7196F4]",
      green: "[--color-text:#166E3F] dark:[--color-text:#6AE1A1]",
      red: "[--color-text:#9A1C13] dark:[--color-text:#F08B85]",
    }
    
    Contrast patterns:
    • Light mode: Dark text on light backgrounds
    • Dark mode: Light text on dark backgrounds
    • Minimum 4.5:1 ratio for normal text
    • Minimum 3:1 ratio for large text

      Interactive States

      Button States

      From packages/components/src/components/badge/badge.tsx:125-144:
      <button
        type="button"
        disabled={disabled}
        className={cn(
          "cursor-pointer transition-opacity hover:opacity-80",
          "data-disabled:cursor-not-allowed",
          "focus-visible:outline-none focus-visible:ring-2"
        )}
      >
        {content}
      </button>
      
      States:
      • Default: Normal appearance
      • Hover: Reduced opacity or color change
      • Focus: Visible focus ring
      • Active: Pressed state
      • Disabled: Reduced opacity, not focusable
      From packages/components/src/components/tooltip/tooltip.tsx:123:
      <a
        href={href}
        className="
          focus-visible:outline-none 
          focus-visible:ring-2 
          focus-visible:ring-primary 
          focus-visible:ring-offset-2
        "
      >
        {cta}
      </a>
      

      Best Practices

        Testing Accessibility

        Tools

        • axe DevTools: Browser extension for automated testing
        • NVDA/JAWS: Screen reader testing on Windows
        • VoiceOver: Screen reader testing on macOS/iOS
        • Keyboard only: Test without using a mouse
        • Wave: Web accessibility evaluation tool

        Manual Testing Checklist

        For more information on WCAG guidelines, visit the W3C Web Accessibility Initiative.

        Next Steps

        Explore component-specific accessibility features:
        • Accordion - Expandable content with keyboard support
        • Tabs - Full keyboard navigation and ARIA support
        • Tooltip - Touch and keyboard accessible tooltips
        • Card - Interactive cards with proper focus management

        Build docs developers (and LLMs) love