Antares
Components

InlineDrawer

An in-flow collapsible panel that expands and collapses along one axis, supporting sidebar navigation and persistent collapsed content.

Features

  • In-flow collapsible panel with CSS transitions
  • minSize keeps collapsed content visible (sidebar nav pattern)
  • shouldDismissOnBlur with portal-aware focus detection
  • Controlled and uncontrolled modes
  • hidden="until-found" for browser find-in-page support
  • prefers-reduced-motion support

Installation

npm install --save @godaddy/antares

Props

NameTypeDefault
placement?InlineDrawerPlacement | undefined'left'

Edge the drawer anchors to. Determines constrained axis. Unlike Drawer, InlineDrawer does not flip in RTL because CSS inline-size/block-size are already logical-direction-aware. The value is physical.

minSize?string | number | undefined

Size when collapsed. Panel stays visible at this size instead of hiding.

maxSize?string | number | undefined

Size when expanded.

animate?boolean | undefinedtrue

CSS transition for expand/collapse. false disables animation.

shouldDismissOnBlur?boolean | undefined

Collapse when focus leaves the drawer. Does not collapse when focus moves to browser chrome.

className?string | undefined

Additional CSS class.

isDisabled?boolean | undefined

Whether the disclosure is disabled.

onExpandedChange?((isExpanded: boolean) => void) | undefined

Handler that is called when the disclosure's expanded state changes.

isExpanded?boolean | undefined

Whether the disclosure is expanded (controlled).

defaultExpanded?boolean | undefined

Whether the disclosure is expanded by default (uncontrolled).

id?Key | undefined

An id for the disclosure when used within a DisclosureGroup, matching the id used in `expandedKeys`.

render?DOMRenderFunction<"div", DisclosureRenderProps> | undefined

Overrides the default DOM element with a custom render function. This allows rendering existing components with built-in styles and behaviors such as router links, animation libraries, and pre-styled components. Requirements: - You must render the expected element type (e.g. if `<button>` is expected, you cannot render an `<a>`). - Only a single root DOM element can be rendered (no fragments). - You must pass through props and ref to the underlying DOM element, merging with your own prop as appropriate.

slot?string | null | undefined

A slot name for the component. Slots allow the component to receive props from a parent component. An explicit `null` value indicates that the local props completely override all props received from a parent.

Examples

Basic Usage

The panel toggles when the trigger is pressed, pushing adjacent content as it expands.

import { InlineDrawer, InlineDrawerTrigger, InlineDrawerPanel, Text } from '@godaddy/antares';export function DefaultExample() {  return (    <InlineDrawer>      <InlineDrawerTrigger>Toggle details</InlineDrawerTrigger>      <InlineDrawerPanel>        <Text>Collapsible content goes here.</Text>      </InlineDrawerPanel>    </InlineDrawer>  );}

Controlled

Use isExpanded and onExpandedChange to drive state externally, e.g. from a button outside the drawer.

Expanded: false

import { useState } from 'react';import { InlineDrawer, InlineDrawerTrigger, InlineDrawerPanel, Button, Text } from '@godaddy/antares';export function ControlledExample() {  const [expanded, setExpanded] = useState(false);  return (    <>      <Button variant="primary" onPress={() => setExpanded(!expanded)}>        {expanded ? 'Collapse' : 'Expand'}      </Button>      <Text>Expanded: {String(expanded)}</Text>      <InlineDrawer isExpanded={expanded} onExpandedChange={setExpanded}>        <InlineDrawerTrigger>Details</InlineDrawerTrigger>        <InlineDrawerPanel>          <Text>Controlled panel content.</Text>        </InlineDrawerPanel>      </InlineDrawer>    </>  );}

Combine minSize and maxSize to create a sidebar that collapses to an icon strip instead of disappearing completely.

Main content area
import { InlineDrawer, InlineDrawerTrigger, InlineDrawerPanel, Flex, Text } from '@godaddy/antares';export function SidebarNavExample() {  return (    <Flex direction="row" style={{ height: 300, border: '1px solid var(--bd-base)' }}>      <InlineDrawer placement="left" defaultExpanded minSize={48} maxSize={220}>        <InlineDrawerTrigger>Menu</InlineDrawerTrigger>        <InlineDrawerPanel>          <Flex as="nav" direction="column" gap="sm" padding="sm">            <Text>Home</Text>            <Text>Settings</Text>            <Text>Profile</Text>          </Flex>        </InlineDrawerPanel>      </InlineDrawer>      <Flex padding="md" style={{ flex: 1 }}>        <Text>Main content area</Text>      </Flex>    </Flex>  );}

Vertical Placement

Set placement="top" or "bottom" to collapse vertically instead of horizontally.

Top drawer content, collapses vertically.
Main content area
import { InlineDrawer, InlineDrawerTrigger, InlineDrawerPanel, Flex, Text } from '@godaddy/antares';export function VerticalExample() {  return (    <Flex direction="column" style={{ height: 400, border: '1px solid var(--bd-base)' }}>      <InlineDrawer placement="top" defaultExpanded minSize={32} maxSize={120}>        <InlineDrawerTrigger>Header</InlineDrawerTrigger>        <InlineDrawerPanel>          <Flex padding="sm">            <Text>Top drawer content, collapses vertically.</Text>          </Flex>        </InlineDrawerPanel>      </InlineDrawer>      <Flex padding="md" style={{ flex: 1 }}>        <Text>Main content area</Text>      </Flex>    </Flex>  );}

Dismiss on Blur

Set shouldDismissOnBlur to auto-collapse when focus leaves the drawer. Tab from the inside button to the outside button to see it collapse. Focus moving to browser chrome (address bar, devtools) does not trigger collapse.

Focus here, then Tab out.
Outside content
import { useState } from 'react';import { InlineDrawer, InlineDrawerTrigger, InlineDrawerPanel, Flex, Text, Button } from '@godaddy/antares';export function DismissOnBlurExample() {  const [expanded, setExpanded] = useState(true);  return (    <Flex direction="row" gap="md" style={{ height: 200 }}>      <InlineDrawer        placement="left"        isExpanded={expanded}        onExpandedChange={setExpanded}        shouldDismissOnBlur        maxSize={220}      >        <InlineDrawerTrigger>Panel</InlineDrawerTrigger>        <InlineDrawerPanel>          <Flex direction="column" gap="sm" padding="sm">            <Text>Focus here, then Tab out.</Text>            <Button variant="secondary" onPress={() => undefined}>              Inside button            </Button>          </Flex>        </InlineDrawerPanel>      </InlineDrawer>      <Flex padding="md" direction="column" gap="sm" style={{ flex: 1 }}>        <Text>Outside content</Text>        <Button variant="secondary" onPress={() => undefined}>          Outside button        </Button>        <Button variant="primary" onPress={() => setExpanded(true)}>          Re-expand        </Button>      </Flex>    </Flex>  );}

Focus Scope

Wrap panel content in <FocusScope> to trap focus while expanded. Tab cycles between Action A and Action B without escaping to main content. Use contain={isExpanded} so trapping only applies when open.

Focus is trapped when expanded.
Main content. Tab cannot reach here while sidebar is expanded.
import { useState } from 'react';import { InlineDrawer, InlineDrawerTrigger, InlineDrawerPanel, FocusScope, Flex, Text, Button } from '@godaddy/antares';export function FocusScopeExample() {  const [expanded, setExpanded] = useState(true);  return (    <Flex direction="row" style={{ height: 200, border: '1px solid var(--bd-base)' }}>      <InlineDrawer placement="left" isExpanded={expanded} onExpandedChange={setExpanded} maxSize={220}>        <InlineDrawerTrigger>Sidebar</InlineDrawerTrigger>        <InlineDrawerPanel>          <FocusScope contain={expanded} restoreFocus>            <Flex direction="column" gap="sm" padding="sm">              <Text>Focus is trapped when expanded.</Text>              <Button variant="secondary" onPress={() => undefined}>                Action A              </Button>              <Button variant="secondary" onPress={() => undefined}>                Action B              </Button>            </Flex>          </FocusScope>        </InlineDrawerPanel>      </InlineDrawer>      <Flex padding="md" style={{ flex: 1 }}>        <Text>Main content. Tab cannot reach here while sidebar is expanded.</Text>        <Button variant="primary" onPress={() => setExpanded(true)}>          Open sidebar        </Button>      </Flex>    </Flex>  );}

Accessibility

Keyboard

KeyAction
Enter / SpaceToggle the drawer via the trigger button
TabMove focus into/out of the panel (or trapped when using FocusScope)

ARIA

  • aria-expanded on the trigger button reflects panel state (via RAC Disclosure)
  • aria-controls links the trigger to the panel
  • hidden="until-found" on collapsed panels (browser find-in-page support)
  • Panels with minSize override aria-hidden to keep collapsed content accessible

Best Practices

  • Use minSize + maxSize for sidebar patterns where collapsed content stays visible.
  • FocusScope is opt-in. Don't combine contain={true} with shouldDismissOnBlur (trapped focus can't leave to trigger blur).
  • Collapsed panels use hidden="until-found" for browser find-in-page support. Panels with minSize override this to keep content visible.
  • Don't nest InlineDrawers.

On this page