Drawer

Open a supporting panel from the edge while preserving the current view's context.

Live preview
Interact with the component to see results here.
View code
HTML
<sy-button id="open-drawer">Open detail panel</sy-button>
<sy-drawer
  closable
  size="custom"
  customsize="280"
  position="right"
  mask-closable
  animation="none"
  id="sample"
><strong slot="header">Project information</strong>
  <div slot="body">
    <p>Owner · Alex Kim</p>
    <p>Status · Under review</p>
  </div>
  <sy-button slot="footer" id="close-drawer">Confirm</sy-button>
</sy-drawer>

<script type="module">
  await ((root) => {
    const panel = root.querySelector('#sample');
    root.querySelector('#open-drawer').addEventListener('click', () => panel.open = true);
    panel.querySelector('#close-drawer').addEventListener('click', () => panel.open = false);
  })(document);
</script>

Usage guide

#

Show details or supporting settings while preserving the current view. Use Modal when a decision is required.

Position and size
Choose a direction with position. For a custom size, use size="custom" and customSize in pixels.
Content structure
Separate the title, details, and actions with the header, body, and footer slots.
Opening and closing
Open with open and configure dismissal through closable and maskClosable.

Examples

#

Detail panel

View item details on the right.

View code
HTML
<sy-button id="open-drawer">Open detail panel</sy-button>
<sy-drawer
  closable
  size="custom"
  customsize="280"
  id="sample"
><strong slot="header">Project information</strong>
  <div slot="body">
    <p>Owner · Alex Kim</p>
    <p>Status · Under review</p>
  </div>
  <sy-button slot="footer" id="close-drawer">Confirm</sy-button>
</sy-drawer>

<script type="module">
  await ((root) => {
    const panel = root.querySelector('#sample');
    root.querySelector('#open-drawer').addEventListener('click', () => panel.open = true);
    panel.querySelector('#close-drawer').addEventListener('click', () => panel.open = false);
  })(document);
</script>

Left panel

Open supporting navigation on the left.

View code
HTML
<sy-button id="open-drawer">Open detail panel</sy-button>
<sy-drawer
  closable
  size="custom"
  customsize="280"
  position="left"
  id="sample"
><strong slot="header">Project information</strong>
  <div slot="body">
    <p>Owner · Alex Kim</p>
    <p>Status · Under review</p>
  </div>
  <sy-button slot="footer" id="close-drawer">Confirm</sy-button>
</sy-drawer>

<script type="module">
  await ((root) => {
    const panel = root.querySelector('#sample');
    root.querySelector('#open-drawer').addEventListener('click', () => panel.open = true);
    panel.querySelector('#close-drawer').addEventListener('click', () => panel.open = false);
  })(document);
</script>

Bottom panel

Place supporting content at the bottom.

View code
HTML
<sy-button id="open-drawer">Open detail panel</sy-button>
<sy-drawer
  closable
  size="custom"
  customsize="240"
  position="bottom"
  id="sample"
><strong slot="header">Project information</strong>
  <div slot="body">
    <p>Owner · Alex Kim</p>
    <p>Status · Under review</p>
  </div>
  <sy-button slot="footer" id="close-drawer">Confirm</sy-button>
</sy-drawer>

<script type="module">
  await ((root) => {
    const panel = root.querySelector('#sample');
    root.querySelector('#open-drawer').addEventListener('click', () => panel.open = true);
    panel.querySelector('#close-drawer').addEventListener('click', () => panel.open = false);
  })(document);
</script>

Keep background visible

Display the panel without a backdrop mask.

View code
HTML
<sy-button id="open-drawer">Open detail panel</sy-button>
<sy-drawer
  closable
  size="custom"
  customsize="280"
  maskless
  id="sample"
><strong slot="header">Project information</strong>
  <div slot="body">
    <p>Owner · Alex Kim</p>
    <p>Status · Under review</p>
  </div>
  <sy-button slot="footer" id="close-drawer">Confirm</sy-button>
</sy-drawer>

<script type="module">
  await ((root) => {
    const panel = root.querySelector('#sample');
    root.querySelector('#open-drawer').addEventListener('click', () => panel.open = true);
    panel.querySelector('#close-drawer').addEventListener('click', () => panel.open = false);
  })(document);
</script>

Enter and exit

Use a slide effect matching position.

View code
HTML
<sy-button id="open-drawer">Open detail panel</sy-button>
<sy-drawer
  closable
  size="custom"
  customsize="280"
  animation="slide"
  id="sample"
><strong slot="header">Project information</strong>
  <div slot="body">
    <p>Owner · Alex Kim</p>
    <p>Status · Under review</p>
  </div>
  <sy-button slot="footer" id="close-drawer">Confirm</sy-button>
</sy-drawer>

<script type="module">
  await ((root) => {
    const panel = root.querySelector('#sample');
    root.querySelector('#open-drawer').addEventListener('click', () => panel.open = true);
    panel.querySelector('#close-drawer').addEventListener('click', () => panel.open = false);
  })(document);
</script>

Events and payloads

Interact with the component and inspect the latest event name and detail. The code includes subscriptions to events emitted by this component and its children.

View code
HTML
<sy-button id="open-drawer">Open detail panel</sy-button>
<sy-drawer
  closable
  size="custom"
  customsize="280"
  position="right"
  mask-closable
  animation="none"
  id="sample"
><strong slot="header">Project information</strong>
  <div slot="body">
    <p>Owner · Alex Kim</p>
    <p>Status · Under review</p>
  </div>
  <sy-button slot="footer" id="close-drawer">Confirm</sy-button>
</sy-drawer>
<output id="demo-events" aria-live="polite">Interact with the component to see
  its latest event and payload.</output>

<script type="module">
  const describeResult = value => JSON.stringify(value, (_key, item) => {
    if (item instanceof File) return {
      name: item.name,
      size: item.size,
      type: item.type
    };
    if (item instanceof Element) return `<${item.localName}>`;
    return item;
  }) ?? 'Done';
  await ((root) => {
    const panel = root.querySelector('#sample');
    root.querySelector('#open-drawer').addEventListener('click', () => panel.open = true);
    panel.querySelector('#close-drawer').addEventListener('click', () => panel.open = false);
  })(document);
  for (const name of ["opened", "closed"]) {
    document.addEventListener(name, event => {
      document.querySelector('#demo-events').textContent =
        `${event.target.localName} · ${name}: ${describeResult(event.detail)}`;
    });
  }
</script>

Close the panel on navigation

closeOnNavigation closes open panels when back or forward navigation triggers popstate. Use it to clear panel state as the app navigates through history.

View code
HTML
<sy-button id="open-drawer">Open detail panel</sy-button>
<sy-drawer
  closable
  size="custom"
  customsize="280"
  position="right"
  mask-closable
  animation="none"
  close-on-navigation
  id="sample"
><strong slot="header">Project information</strong>
  <div slot="body">
    <p>Owner · Alex Kim</p>
    <p>Status · Under review</p>
  </div>
  <sy-button slot="footer" id="close-drawer">Confirm</sy-button>
</sy-drawer>

<script type="module">
  const component1 = document.querySelector("#sample");
  Object.assign(component1, {
    "closeOnNavigation": true
  });
  await ((root) => {
    const panel = root.querySelector('#sample');
    root.querySelector('#open-drawer').addEventListener('click', () => panel.open = true);
    panel.querySelector('#close-drawer').addEventListener('click', () => panel.open = false);
  })(document);
</script>

Top drawer

Open a compact drawer from the top of the preview.

View code
HTML
<sy-button id="open-drawer">Open detail panel</sy-button>
<sy-drawer
  closable
  size="custom"
  customsize="160"
  position="top"
  mask-closable
  animation="none"
  id="sample"
><strong slot="header">Project information</strong>
  <div slot="body">
    <p>Owner · Alex Kim</p>
    <p>Status · Under review</p>
  </div>
  <sy-button slot="footer" id="close-drawer">Confirm</sy-button>
</sy-drawer>

<script type="module">
  const component1 = document.querySelector("#sample");
  Object.assign(component1, {
    "position": "top",
    "size": "custom",
    "customSize": 160
  });
  await ((root) => {
    const panel = root.querySelector('#sample');
    root.querySelector('#open-drawer').addEventListener('click', () => panel.open = true);
    panel.querySelector('#close-drawer').addEventListener('click', () => panel.open = false);
  })(document);
</script>

API reference

#

Hover over a name or focus it with the keyboard to see the code. On touch screens, tap the name.

<sy-drawer>

Properties

Attribute / PropertyTypeDefaultDescription
booleanfalseOmit the backdrop mask.
booleanfalsePrevent user dismissal. Close it from the app with open=false.
booleanfalseShow the close button.
booleanfalseWhether the submenu is expanded.
number100Panel width or height in pixels when size=custom.
'top' | 'left' | 'right' | 'bottom''right'Screen edge from which the panel appears.
'small' | 'medium' | 'large' | 'custom''medium'Component size.
booleantrueClose the panel when the backdrop is clicked.
booleantrueWhen true, drawer closes on browser history navigation. Spec default: true.
string''id of the parent element that contains the panel.
| 'none' | 'fade' | 'fade-scale' | 'zoom' | 'pop' | 'slide' | 'slide-fade' | 'reveal' | 'slide-top' | 'slide-bottom' | 'slide-left' | 'slide-right' | 'slide-fade-top' | 'slide-fade-bottom' | 'slide-fade-left' | 'slide-fade-right' | 'reveal-top' | 'reveal-bottom' | 'reveal-left' | 'reveal-right' | 'flip-x' | 'flip-y' | 'bounce' | 'spin''none'Enter/exit animation type. `none` (default) toggles instantly. Three flavors of direction handling: - **Auto** (`slide`, `slide-fade`, `reveal`): direction follows the `position` attribute (a right-anchored drawer slides from the right). - **Explicit** (`slide-{top,bottom,left,right}`, `slide-fade-…`, `reveal-…`): direction is fixed regardless of `position`. - **Direction-less**: plays the same way no matter the orientation. Some combinations (e.g. `zoom` on an edge-anchored panel) look unusual but are exposed for completeness — selection is the consumer's call.
'x-fast' | 'fast' | 'medium' | 'slow' | 'x-slow''fast'Animation playback speed token. Maps to `--transition-*` global tokens.

Events

EventDetailDescription
CustomEvent<void>Emitted when the list opens.
CustomEvent<void>Read the result from the event's detail.

Slots

SlotDescription
Content displayed in the header area.
Content displayed in the body area.
Content displayed in the footer area.