Sheet

The Sheet component is a container that acts as a supplementary view, providing access to important contextual information. It can be used to display additional content or functionality in mobile views replacing Modal from desktop.

Available from eds-core/1.10.0

Quick Start

Installation
npm install @adaptavant/eds-core
Import
import { Sheet } from '@adaptavant/eds-core';

Basic Usage

All Sheet_Primitives come with default styling for spacing and overflow scroll behavior, which can be customized using Style APIs (className, classNames, style, styles) if needed.

Only Sheet_Primitives like SheetTitle, SheetContent, and SheetFooter are accepted as direct children; passing any other child will result in an error.

Note: There is a default 500 milliseconds delay for the sheet animation.

const [openSheet, setOpenSheet] = React.useState(false);

function onSheetOpen() {
  setOpenSheet(true);
  console.log("sheet opened");
}

function onSheetClose() {
  setOpenSheet(false);
  console.log("sheet close");
}

return (
  <React.Fragment>
    <Button onClick={onSheetOpen}>Open Sheet</Button>
    <Sheet
      descriptionId="sheet-description"
      onClose={onSheetClose}
      open={openSheet}
      titleId="sheet-title"
      closeButtonProps={{
          label: "Sheet Close Button",
          onClick: onSheetClose,
        }}
    >
      <SheetHeader>
        <Heading as="h2" className="text-heading-16" id="sheet-title">
          Title of sheet
        </Heading>
        <Text className="text-body-14">
          Sub header of the sheet if needed can be added as aria describedby
        </Text>
      </SheetHeader>
      <SheetContent className="gap-2 flex flex-col">
        <Text className="text-body-16">
          Slot for any content You can decide or partiction what goes inside
          these slots, below you can see content or forms for example.
        </Text>
        <Text className="text-body-14" id="sheet-description">
          Body of Sheet - announced as aria describedby
        </Text>
        <Field label="Name">
          <TextInput defaultValue="Test 123123" />
        </Field>
      </SheetContent>
      <SheetFooter>
        <Text className="mr-auto text-body-12 text-secondary">
          This portion also a slot
        </Text>
        <Button onClick={onSheetClose} variant="neutralTertiary">
          Cancel
        </Button>
        <Button onClick={onSheetClose}>Save</Button>
      </SheetFooter>
    </Sheet>
  </React.Fragment>
);

Placement

The placement prop determines the position of the Sheet component relative to its trigger element. This prop allows for flexible layout options, enabling the Sheet to be displayed from various directions:

  • 'left': Positions the Sheet to the left of the trigger element.
  • 'right': Positions the Sheet to the right of the trigger element.
  • 'top': Positions the Sheet above the trigger element.
  • 'bottom': Positions the Sheet below the trigger element (default).

Note: Choose the appropriate placement can enhance user experience by optimizing visibility and accessibility based on the layout of the surrounding components.

const [openSheet, setOpenSheet] = React.useState(false);


function onSheetOpen() {
    setOpenSheet(true);
    console.log('sheet opened');
}

function onSheetClose() {
    setOpenSheet(false);
    console.log('sheet close');
}

return (
  <React.Fragment>
    <Button onClick={onSheetOpen}>Open Sheet</Button>
    <Sheet 
      className="top-[60px]" // Adjusts sheet position to account for the fixed header on the docs page
      descriptionId="sheet-description"
      onClose={onSheetClose}
      open={openSheet}
      titleId="sheet-title"
      placement="top"
      closeButtonProps={{
              label: "Sheet Close Button",
              onClick: onSheetClose,
            }}
    >
      <SheetHeader>
        <Heading as="h2" className="text-heading-16" id="sheet-title">
          Title of sheet
        </Heading>
        <Text className="text-body-14">
          Sub header of the sheet if needed can be added as aria describedby
        </Text>
      </SheetHeader>
      <SheetContent className="gap-2 flex flex-col">
        <Text className="text-body-16">
          Slot for any content You can decide or partiction what goes inside
          these slots, below you can see content or forms for example.
        </Text>
        <Text className="text-body-14" id="sheet-description">
          Body of Sheet - announced as aria describedby
        </Text>
        <Field label="Name">
          <TextInput defaultValue="Test 123123" />
        </Field>
      </SheetContent>
      <SheetFooter>
        <Text className="mr-auto text-body-12 text-secondary">
          This portion also a slot
        </Text>
        <Button onClick={onSheetClose} variant="neutralTertiary">
          Cancel
        </Button>
        <Button onClick={onSheetClose}>Save</Button>
      </SheetFooter>
    </Sheet>
  </React.Fragment>
);

Full Screen

When isFullScreen is true, the sheet will take up 100% of the available height and width based on placement prop and corner radii will be removed.

const [openSheet, setOpenSheet] = React.useState(false);


function onSheetOpen() {
setOpenSheet(true);
console.log('sheet opened');
}

function onSheetClose() {
setOpenSheet(false);
console.log('sheet close');
}

return (
<React.Fragment>
  <Button onClick={onSheetOpen}>Open Sheet</Button>
  <Sheet className="top-[60px]" // Adjusts sheet position to account for the fixed header on the docs page
    descriptionId="sheet-description" onClose={onSheetClose} isFullScreen open={openSheet} titleId="sheet-title"
    placement="bottom"
    closeButtonProps={{ label: "Sheet Close Button" , onClick: onSheetClose, }}
    >
    <SheetHeader>
      <Heading as="h2" className="text-heading-16" id="sheet-title">
        Title of sheet
      </Heading>
      <Text className="text-body-14">
        Sub header of the sheet if needed can be added as aria describedby
      </Text>
    </SheetHeader>
    <SheetContent className="gap-2 flex flex-col">
      <Text className="text-body-16">
        Slot for any content You can decide or partiction what goes inside
        these slots, below you can see content or forms for example.
      </Text>
      <Text className="text-body-14" id="sheet-description">
        Body of Sheet - announced as aria describedby
      </Text>
      <Field label="Name">
        <TextInput defaultValue="Test 123123" />
      </Field>
    </SheetContent>
    <SheetFooter>
      <Text className="mr-auto text-body-12 text-secondary">
        This portion also a slot
      </Text>
      <Button onClick={onSheetClose} variant="neutralTertiary">
        Cancel
      </Button>
      <Button onClick={onSheetClose}>Save</Button>
    </SheetFooter>
  </Sheet>
</React.Fragment>
);

API Reference

Sheet

PropsTypeDescriptionDefault
childrenSheetChildren | SheetChildren[]The content of the sheet, typically including header, content, and footer._
openbooleanWhen set to true, the sheet will be mounted to DOM._

titleId?

string

Id of Modal Title element, will be announced as aria-labelledby

Note: When <ModalHeader /> is not used, avoid using this prop. Instead, provide an ariaLabel for an accessible name.

_

onClosefunctionCallback triggered on closing the sheet and will activate upon pressing "Esc" or clicking on the overlay._
placement?'left' | 'right' | 'top' | 'bottom'Determines the position of the sheet on the screen.bottom
isFullScreen?booleanWhen true, the sheet will take up 100% of the available height or width based on placement prop and corner radii will be removed.false
onEscPress?(event: KeyboardEvent) => voidCallback fired when "Esc" key is pressed along with onClose._
onOverlayClick?functionCallback fired when the overlay is clicked along with onClose_
descriptionId?stringId of Sheet description element, will be announced as aria-describedby_
closeOnEsc?booleanIf set to false, the sheet will not close when the Esc key is pressed, and the Esc keydown event along with the onClose callback won't be attached. Similarly, onEscPress will have no effect.true
closeOnOverlayClick?booleanIf set to false, the sheet will not close when the overlay is clicked, and the onClose callback won’t be attached. Similarly, onOverlayClick will have no effect.true
role?'dialog' | 'alertdialog'The role attribute of the sheet.dialog
shouldContainFocus?booleanIf users need to interact with other parts of the page such as a chatbot window , disable focus containment within the sheet by setting this prop to false.

Note: This is not recommended by default, as it may impact WCAG accessibility compliance.
true

ariaLabel?

string

Descriptive label for modal , will be announced as aria-label

Note: Use this prop only when <ModalHeader /> is not needed .

_

closeButtonProps?objectTo render and customise Modal close button contains label and onClick_
closeButtonProps.labelstringAria label content for the close Icon button_
closeButtonProps.onClickfunctionCallback function to be invoked when the close button is clicked._

SheetHeader

PropTypeDescriptionDefault
closeButtonProps?objectTo render and customise Sheet close button contains label and onClick_
closeButtonProps.labelstringAria label content for the close Icon button._
closeButtonProps.onClickfunctionCallback function to be invoked when the close button is clicked._

Note: closeButtonProps is deprecated as of version 1.25.3. It is recommended to use the same prop directly on the <Modal /> component instead.

Style API

Our design system components include style props that allow you to easily customize different parts of each component to match your design needs.

Please refer to the Style API documentation for more insights.

Sheet parts

const [openSheet, setOpenSheet] = React.useState(false);


function onSheetOpen() {
    setOpenSheet(true);
    console.log('sheet opened');
}

function onSheetClose() {
    setOpenSheet(false);
    console.log('sheet close');
}

return (
  <React.Fragment>
    <Button onClick={onSheetOpen}>Open Sheet</Button>
    <Sheet
      descriptionId="sheet-description"
      onClose={onSheetClose}
      open={openSheet}
      titleId="sheet-title"
      closeButtonProps={{
          label: "Sheet Close Button",
          onClick: onSheetClose,
        }}
      classNames={{ sheetWrapper: "text-positive",closeButton:"bg-critical" }}
    >
      <SheetHeader>
        <Heading as="h2" className="text-heading-16" id="sheet-title">
          Title of sheet
        </Heading>
      </SheetHeader>
      <SheetContent className="gap-2 flex flex-col">
        <Text className="text-body-16">
          Slot for any content You can decide or partiction what goes inside
          these slots, below you can see content or forms for example.
        </Text>
      </SheetContent>
      <SheetFooter>
        <Button onClick={onSheetClose}>Save</Button>
      </SheetFooter>
    </Sheet>
  </React.Fragment>
);
Stylable PartsDescription
sheetWrapperThe inner wrapper of the sheet, typically used for controlling the layout or the sheet content's alignment and size.
closeButtonThe button element used to close the sheet, typically placed at the top-right corner of the dialog.

SheetHeader parts

const [openSheet, setOpenSheet] = React.useState(false);


function onSheetOpen() {
    setOpenSheet(true);
    console.log('sheet opened');
}

function onSheetClose() {
    setOpenSheet(false);
    console.log('sheet close');
}

return (
  <React.Fragment>
    <Button onClick={onSheetOpen}>Open Sheet</Button>
    <Sheet
      descriptionId="sheet-description"
      onClose={onSheetClose}
      open={openSheet}
      titleId="sheet-title"
    >
       <SheetHeader
            closeButtonProps={{
              label: "Sheet Close Button",
              onClick: onSheetClose,
            }}
            classNames={{ closeButton: "bg-neutral-secondary" }}
          >
        <Heading as="h2" className="text-heading-16" id="sheet-title">
          Title of sheet
        </Heading>
      </SheetHeader>
      <SheetContent className="gap-2 flex flex-col">
        <Text className="text-body-16">
          Slot for any content You can decide or partiction what goes inside
          these slots, below you can see content or forms for example.
        </Text>
      </SheetContent>
      <SheetFooter>
        <Button onClick={onSheetClose}>Save</Button>
      </SheetFooter>
    </Sheet>
  </React.Fragment>
);
Stylable PartsDescription
closeButton(Deprecated)The button element used to close the sheet, typically placed at the top-right corner of the header.

Usage guidelines

Do

  1. Display contextual information: Do use the sheet for displaying contextual actions or additional information that enhances the main page without disrupting the user’s flow (e.g., clicking on a table row).
  2. Use for subtasks: Do use the sheet to handle subtasks that are too complex for a popover but should remain within the context of the main task.
  3. Maintain user context: Do allow users to maintain context by letting them interact with secondary functions without leaving the current page, such as linking external resources or batch updating.
  4. Ensure screen space awareness: Do consider screen real estate, especially when multiple sheets or side panels are present. Overlapping or overwhelming users with shrinking page content should be avoided.

Don’t

  1. Avoid nested drawers: Don’t open one drawer from within another. Only one sheet should be open at a time to avoid confusion and complexity.
  2. Don’t use for complex forms or complex interactions: Don’t use a sheet for complex forms instead, move the content to a dedicated page for better clarity.

Best practices

Do

Swap modals for sheets on mobile: Use sheets instead of modals on mobile devices to reduce excessive padding and maximize space for content.

Don’t

Don’t use modals on mobile: Avoid using modals with large paddings that take up too much screen space, as sheets offer a more efficient layout.

Do

Prioritize popular choices: Arrange the most important actions from top to bottom, making the most commonly used options easier to access.

Don’t

Don’t bury important actions: Avoid placing critical or frequently used options lower in the list, where they may be harder to find.

Do

Ensure clear header and close actions: Do always include a clear, descriptive header and provide "Done" and "Close" actions to help users understand the sheet's purpose and exit it easily.

Don’t

Don't limit options: Allow users to make choices and avoid restricting them to a single action by providing only one option.

Do

Consider content complexity: Use a sheet size that matches the complexity of the content. For most scenarios, a medium-sized sheet is ideal to balance information and usability.

Don’t

Don’t use oversized sheets for simple tasks: Avoid using large sheets when the content is minimal or straightforward. Opt for smaller components like popovers or dropdowns in such cases.

Do

Ensure screen space awareness: Consider the available screen real estate, especially when multiple components are present.

Don’t

Don’t overload the interface: Avoid displaying too many components at once, as it can shrink content areas and confuse users by overcrowding the page. Avoid crowding the screen and overwhelming users.