# Content and Accessibility

Main content container with focus management and accessibility components for proper dialog implementation.

## Capabilities

### DialogContent

Primary content container that handles focus management, keyboard navigation, and dismissal behavior.

```typescript { .api }
/**
 * Main content container for dialog with comprehensive accessibility features
 * Handles focus trapping, keyboard navigation, and dismissal behavior
 */
type DialogContentElement = React.ComponentRef<typeof Primitive.div>;
interface DialogContentProps extends DialogContentTypeProps {
  /** Force mounting when more control is needed for animations */
  forceMount?: true;
}

interface DialogContentTypeProps extends Omit<DismissableLayerProps, 'onDismiss'> {
  /**
   * When `true`, focus cannot escape the `Content` via keyboard,
   * pointer, or a programmatic focus.
   * @defaultValue false (for non-modal), true (for modal)
   */
  trapFocus?: boolean;
  
  /**
   * When `true`, hover/focus/click interactions will be disabled on elements outside
   * the `DismissableLayer`. Users will need to click twice on outside elements to
   * interact with them: once to close the `DismissableLayer`, and again to trigger the element.
   */
  disableOutsidePointerEvents?: boolean;
  
  /**
   * Event handler called when auto-focusing on open.
   * Can be prevented.
   */
  onOpenAutoFocus?: (event: Event) => void;
  
  /**
   * Event handler called when auto-focusing on close.
   * Can be prevented.
   */
  onCloseAutoFocus?: (event: Event) => void;
  
  /**
   * Event handler called when the escape key is down.
   * Can be prevented.
   */
  onEscapeKeyDown?: (event: KeyboardEvent) => void;
  
  /**
   * Event handler called when the a `pointerdown` event happens outside of the `DismissableLayer`.
   * Can be prevented.
   */
  onPointerDownOutside?: (event: PointerDownOutsideEvent) => void;
  
  /**
   * Event handler called when the focus moves outside of the `DismissableLayer`.
   * Can be prevented.
   */
  onFocusOutside?: (event: FocusOutsideEvent) => void;
  
  /**
   * Event handler called when an interaction happens outside the `DismissableLayer`.
   * Specifically, when a `pointerdown` event happens outside or focus moves outside of it.
   * Can be prevented.
   */
  onInteractOutside?: (event: PointerDownOutsideEvent | FocusOutsideEvent) => void;
}

// Event types from DismissableLayer
type PointerDownOutsideEvent = CustomEvent<{ originalEvent: PointerEvent }>;
type FocusOutsideEvent = CustomEvent<{ originalEvent: FocusEvent }>;

type DismissableLayerProps = React.ComponentPropsWithoutRef<typeof Primitive.div> & {
  disableOutsidePointerEvents?: boolean;
  onEscapeKeyDown?: (event: KeyboardEvent) => void;
  onPointerDownOutside?: (event: PointerDownOutsideEvent) => void;
  onFocusOutside?: (event: FocusOutsideEvent) => void;
  onInteractOutside?: (event: PointerDownOutsideEvent | FocusOutsideEvent) => void;
  onDismiss?: () => void;
};

const DialogContent: React.ForwardRefExoticComponent<
  DialogContentProps & React.RefAttributes<DialogContentElement>
>;
```

**Usage Examples:**

```typescript
import { 
  Dialog, 
  DialogTrigger, 
  DialogPortal,
  DialogOverlay,
  DialogContent,
  DialogTitle,
  DialogDescription,
  DialogClose
} from "@radix-ui/react-dialog";

// Basic content
function BasicContent() {
  return (
    <Dialog>
      <DialogTrigger>Open</DialogTrigger>
      <DialogPortal>
        <DialogOverlay />
        <DialogContent>
          <DialogTitle>Dialog Title</DialogTitle>
          <DialogDescription>Dialog description here</DialogDescription>
          <DialogClose>Close</DialogClose>
        </DialogContent>
      </DialogPortal>
    </Dialog>
  );
}

// Content with custom focus handling
function CustomFocusContent() {
  const handleOpenAutoFocus = (event: Event) => {
    // Prevent default auto-focus and focus specific element
    event.preventDefault();
    const firstInput = document.querySelector('input');
    firstInput?.focus();
  };

  const handleCloseAutoFocus = (event: Event) => {
    // Custom focus restoration
    event.preventDefault();
    const customButton = document.getElementById('custom-trigger');
    customButton?.focus();
  };

  return (
    <Dialog>
      <DialogTrigger id="custom-trigger">Open</DialogTrigger>
      <DialogPortal>
        <DialogOverlay />
        <DialogContent 
          onOpenAutoFocus={handleOpenAutoFocus}
          onCloseAutoFocus={handleCloseAutoFocus}
        >
          <DialogTitle>Custom Focus Dialog</DialogTitle>
          <input placeholder="This gets focused on open" />
          <DialogClose>Close</DialogClose>
        </DialogContent>
      </DialogPortal>
    </Dialog>
  );
}

// Content with outside interaction handling
function OutsideInteractionContent() {
  const handlePointerDownOutside = (event: PointerDownOutsideEvent) => {
    console.log('Pointer down outside dialog');
    // Prevent default to stop dialog from closing
    // event.preventDefault();
  };

  const handleInteractOutside = (event: PointerDownOutsideEvent | FocusOutsideEvent) => {
    // Handle any interaction outside the dialog
    if (event.target?.closest('.keep-open-trigger')) {
      event.preventDefault(); // Don't close when clicking this element
    }
  };

  return (
    <Dialog>
      <DialogTrigger>Open</DialogTrigger>
      <DialogPortal>
        <DialogOverlay />
        <DialogContent 
          onPointerDownOutside={handlePointerDownOutside}
          onInteractOutside={handleInteractOutside}
        >
          <DialogTitle>Outside Interaction Dialog</DialogTitle>
          <p>Try clicking outside or pressing ESC</p>
          <DialogClose>Close</DialogClose>
        </DialogContent>
      </DialogPortal>
    </Dialog>
  );
}

// Non-modal content
function NonModalContent() {
  return (
    <Dialog modal={false}>
      <DialogTrigger>Open Non-Modal</DialogTrigger>
      <DialogPortal>
        <DialogContent 
          trapFocus={false}
          disableOutsidePointerEvents={false}
        >
          <DialogTitle>Non-Modal Dialog</DialogTitle>
          <p>Focus is not trapped, background remains interactive</p>
          <DialogClose>Close</DialogClose>
        </DialogContent>
      </DialogPortal>
    </Dialog>
  );
}

// Modal content with disabled outside pointer events
function DisabledOutsidePointerEventsContent() {
  return (
    <Dialog>
      <DialogTrigger>Open Modal</DialogTrigger>
      <DialogPortal>
        <DialogOverlay />
        <DialogContent 
          trapFocus={true}
          disableOutsidePointerEvents={true}
        >
          <DialogTitle>Modal Dialog</DialogTitle>
          <p>Outside interactions require two clicks: one to close dialog, one to activate element</p>
          <DialogClose>Close</DialogClose>
        </DialogContent>
      </DialogPortal>
    </Dialog>
  );
}
```

### DialogTitle

Title component for dialog accessibility, automatically linked via ARIA attributes.

```typescript { .api }
/**
 * Dialog title component for proper accessibility labeling
 * Automatically referenced by DialogContent via aria-labelledby
 */
type DialogTitleElement = React.ComponentRef<typeof Primitive.h2>;
interface DialogTitleProps extends React.ComponentPropsWithoutRef<typeof Primitive.h2> {}

const DialogTitle: React.ForwardRefExoticComponent<
  DialogTitleProps & React.RefAttributes<DialogTitleElement>
>;
```

**Usage Examples:**

```typescript
// Basic title
function BasicTitle() {
  return (
    <Dialog>
      <DialogTrigger>Open</DialogTrigger>
      <DialogPortal>
        <DialogOverlay />
        <DialogContent>
          <DialogTitle>Confirm Delete</DialogTitle>
          <p>Are you sure you want to delete this item?</p>
        </DialogContent>
      </DialogPortal>
    </Dialog>
  );
}

// Styled title
function StyledTitle() {
  return (
    <Dialog>
      <DialogTrigger>Open</DialogTrigger>
      <DialogPortal>
        <DialogOverlay />
        <DialogContent>
          <DialogTitle className="dialog-title" style={{ fontSize: '24px' }}>
            Settings
          </DialogTitle>
          <p>Configure your application settings</p>
        </DialogContent>
      </DialogPortal>
    </Dialog>
  );
}

// Visually hidden title (for accessibility only)
function VisuallyHiddenTitle() {
  return (
    <Dialog>
      <DialogTrigger>Open</DialogTrigger>
      <DialogPortal>
        <DialogOverlay />
        <DialogContent>
          <DialogTitle className="sr-only">
            Image Gallery
          </DialogTitle>
          {/* Visual content without visible title */}
          <div className="image-gallery">
            {/* Images here */}
          </div>
        </DialogContent>
      </DialogPortal>
    </Dialog>
  );
}
```

### DialogDescription

Description component for dialog accessibility, automatically linked via ARIA attributes.

```typescript { .api }
/**
 * Dialog description component for additional accessibility context
 * Automatically referenced by DialogContent via aria-describedby
 */
type DialogDescriptionElement = React.ComponentRef<typeof Primitive.p>;
interface DialogDescriptionProps extends React.ComponentPropsWithoutRef<typeof Primitive.p> {}

const DialogDescription: React.ForwardRefExoticComponent<
  DialogDescriptionProps & React.RefAttributes<DialogDescriptionElement>
>;
```

**Usage Examples:**

```typescript
// Basic description
function BasicDescription() {
  return (
    <Dialog>
      <DialogTrigger>Delete Item</DialogTrigger>
      <DialogPortal>
        <DialogOverlay />
        <DialogContent>
          <DialogTitle>Confirm Deletion</DialogTitle>
          <DialogDescription>
            This action cannot be undone. The item will be permanently removed.
          </DialogDescription>
          <div className="dialog-actions">
            <DialogClose>Cancel</DialogClose>
            <DialogClose>Delete</DialogClose>
          </div>
        </DialogContent>
      </DialogPortal>
    </Dialog>
  );
}

// Multiple descriptions
function MultipleDescriptions() {
  return (
    <Dialog>
      <DialogTrigger>Show Details</DialogTrigger>
      <DialogPortal>
        <DialogOverlay />
        <DialogContent>
          <DialogTitle>User Profile</DialogTitle>
          <DialogDescription>
            View and edit your profile information below.
          </DialogDescription>
          <DialogDescription className="warning">
            Changes will be saved automatically.
          </DialogDescription>
          <form>
            {/* Form fields */}
          </form>
        </DialogContent>
      </DialogPortal>
    </Dialog>
  );
}

// Optional description
function OptionalDescription() {
  const [showDetails, setShowDetails] = React.useState(false);
  
  return (
    <Dialog>
      <DialogTrigger>Open</DialogTrigger>
      <DialogPortal>
        <DialogOverlay />
        <DialogContent aria-describedby={showDetails ? undefined : undefined}>
          <DialogTitle>Simple Dialog</DialogTitle>
          {showDetails && (
            <DialogDescription>
              Additional details when needed
            </DialogDescription>
          )}
          <button onClick={() => setShowDetails(!showDetails)}>
            {showDetails ? 'Hide' : 'Show'} Details
          </button>
        </DialogContent>
      </DialogPortal>
    </Dialog>
  );
}
```

## Accessibility Features

### DialogContent Accessibility

DialogContent automatically provides:

- `role="dialog"` for proper semantic role
- `aria-labelledby` referencing DialogTitle ID
- `aria-describedby` referencing DialogDescription ID
- `data-state` attribute with "open" or "closed" values
- Focus management and trapping
- Keyboard navigation (ESC to close)

### Focus Management

#### Auto Focus Behavior

**On Open:**
1. Focus moves to first focusable element in DialogContent
2. If no focusable elements, focus goes to DialogContent itself
3. Can be customized with `onOpenAutoFocus`

**On Close:**
1. Focus returns to DialogTrigger that opened the dialog
2. Can be customized with `onCloseAutoFocus`

#### Focus Trapping

For modal dialogs:
- Tab cycles through focusable elements within DialogContent
- Shift+Tab cycles backwards
- Focus cannot escape the dialog via keyboard

### Keyboard Navigation

- **ESC**: Closes the dialog
- **Tab/Shift+Tab**: Navigate focusable elements
- **Enter/Space**: Activate buttons and controls

### Screen Reader Support

- Dialog is announced when opened
- Title and description are read to users
- Focus changes are announced
- State changes (open/closed) are communicated

## Development Warnings

In development mode, Radix UI provides helpful warnings:

- Missing DialogTitle warns in console
- Missing DialogDescription warns if aria-describedby is not undefined
- These warnings help ensure accessibility compliance

```typescript
// This will warn in development
<DialogContent>
  {/* Missing DialogTitle */}
  <p>Content without title</p>
</DialogContent>

// This will not warn
<DialogContent aria-describedby={undefined}>
  <DialogTitle>Title Present</DialogTitle>
  <p>Content without description (intentional)</p>
</DialogContent>
```