Drawer

An edge-anchored panel for side drawers, filters, and quick captures.

Basic

A trigger opens the panel via data-stisla-drawer-trigger="<id>". The panel itself is a .drawer with an optional .drawer__header, a .drawer__body, and an optional .drawer__footer. The dismiss control is .drawer__close, an inline ghost icon button at the trailing edge of the header row. Default placement is the right edge.

New task

<button type="button" class="btn btn--primary" data-stisla-drawer-trigger="drawerBasic">
  New task
</button>

<div class="drawer" data-stisla-drawer id="drawerBasic" aria-labelledby="drawerBasicLabel">
  <div class="drawer__backdrop" data-stisla-drawer-dismiss></div>
  <div class="drawer__content">
    <div class="drawer__header">
      <h3 class="drawer__title" id="drawerBasicLabel">New task</h3>
      <button type="button" class="drawer__close" data-stisla-drawer-dismiss aria-label="Close">
        <i data-lucide="x"></i>
      </button>
    </div>
    <div class="drawer__body">
      <div class="mb-4">
        <label for="taskTitle" class="form-label">Title</label>
        <input type="text" class="input" id="taskTitle" placeholder="Write the launch announcement">
      </div>
      <div class="mb-4">
        <label for="taskDesc" class="form-label">Description</label>
        <textarea class="textarea" id="taskDesc" rows="4" placeholder="Anything the assignee should know before they start."></textarea>
      </div>
      <div class="row g-3">
        <div class="col-6">
          <label for="taskDue" class="form-label">Due</label>
          <input type="date" class="input" id="taskDue">
        </div>
        <div class="col-6">
          <label for="taskPriority" class="form-label">Priority</label>
          <select class="select" id="taskPriority">
            <option>Low</option>
            <option selected>Medium</option>
            <option>High</option>
          </select>
        </div>
      </div>
    </div>
    <div class="drawer__footer">
      <button type="button" class="btn btn--ghost btn--neutral" data-stisla-drawer-dismiss>Cancel</button>
      <button type="button" class="btn btn--primary" data-stisla-drawer-dismiss>Create task</button>
    </div>
  </div>
</div>

Placements

Four modifiers anchor the panel to a viewport edge. .drawer--start slides in from the left, .drawer--end from the right, .drawer--top from above, .drawer--bottom from below. Start and end take a fixed width, top and bottom take a fixed height. Bare .drawer behaves as end.

Account

  • Profile
    Name, avatar, bio
  • Notifications
    Email, push, digest
  • Billing
    Plan, invoices, payment method
  • Security
    Password, 2FA, sessions

Notifications

Deploy finished

Build #2147 shipped to production in 4m 12s.

2 minutes ago
Amelia replied

"Let's pair on the cart bug tomorrow morning."

12 minutes ago
Disk above 80%

db-primary-2 is at 84% used. Consider expanding the volume.

35 minutes ago

What's new in June

New
Bulk reassign

Move many tasks to a new owner in one go from the board view.

Improved
Faster search

Workspace search now returns results in under 100ms for most queries.

Fixed
Recurring task DST

Recurring tasks no longer drift by an hour around daylight saving.

Share this report

<div class="d-flex flex-wrap gap-2">
  <button type="button" class="btn btn--outline btn--neutral" data-stisla-drawer-trigger="drawerStart">Start</button>
  <button type="button" class="btn btn--outline btn--neutral" data-stisla-drawer-trigger="drawerEnd">End</button>
  <button type="button" class="btn btn--outline btn--neutral" data-stisla-drawer-trigger="drawerTop">Top</button>
  <button type="button" class="btn btn--outline btn--neutral" data-stisla-drawer-trigger="drawerBottom">Bottom</button>
</div>

<div class="drawer drawer--start" data-stisla-drawer id="drawerStart">
  <div class="drawer__backdrop" data-stisla-drawer-dismiss></div>
  <div class="drawer__content">
    <div class="drawer__header">
      <h3 class="drawer__title">Account</h3>
      <button type="button" class="drawer__close" data-stisla-drawer-dismiss aria-label="Close"><i data-lucide="x"></i></button>
    </div>
    <div class="drawer__body p-0">
      <ul class="list-group list-group--flush">
        <li class="list-group__item d-flex align-items-center gap-3">
          <span class="icon-box icon-box--primary"><i data-lucide="user"></i></span>
          <div class="flex-fill">
            <div class="fw-medium">Profile</div>
            <div class="fs-2 text-muted-foreground">Name, avatar, bio</div>
          </div>
          <i data-lucide="chevron-right" width="18" class="text-muted-foreground"></i>
        </li>
        <li class="list-group__item d-flex align-items-center gap-3">
          <span class="icon-box icon-box--info"><i data-lucide="bell"></i></span>
          <div class="flex-fill">
            <div class="fw-medium">Notifications</div>
            <div class="fs-2 text-muted-foreground">Email, push, digest</div>
          </div>
          <i data-lucide="chevron-right" width="18" class="text-muted-foreground"></i>
        </li>
        <li class="list-group__item d-flex align-items-center gap-3">
          <span class="icon-box icon-box--warning"><i data-lucide="credit-card"></i></span>
          <div class="flex-fill">
            <div class="fw-medium">Billing</div>
            <div class="fs-2 text-muted-foreground">Plan, invoices, payment method</div>
          </div>
          <i data-lucide="chevron-right" width="18" class="text-muted-foreground"></i>
        </li>
        <li class="list-group__item d-flex align-items-center gap-3">
          <span class="icon-box"><i data-lucide="shield"></i></span>
          <div class="flex-fill">
            <div class="fw-medium">Security</div>
            <div class="fs-2 text-muted-foreground">Password, 2FA, sessions</div>
          </div>
          <i data-lucide="chevron-right" width="18" class="text-muted-foreground"></i>
        </li>
      </ul>
    </div>
  </div>
</div>

<div class="drawer drawer--end" data-stisla-drawer id="drawerEnd">
  <div class="drawer__backdrop" data-stisla-drawer-dismiss></div>
  <div class="drawer__content">
    <div class="drawer__header">
      <h3 class="drawer__title">Notifications</h3>
      <button type="button" class="drawer__close" data-stisla-drawer-dismiss aria-label="Close"><i data-lucide="x"></i></button>
    </div>
    <div class="drawer__body">
      <div class="d-flex gap-3 mb-4 pb-4" style="border-bottom: 1px solid var(--st-border);">
        <span class="icon-box icon-box--success icon-box--round flex-shrink-0"><i data-lucide="check"></i></span>
        <div>
          <div class="fw-medium">Deploy finished</div>
          <p class="text-muted-foreground m-0 mb-1 fs-2">Build #2147 shipped to production in 4m 12s.</p>
          <span class="fs-2 text-muted-foreground">2 minutes ago</span>
        </div>
      </div>
      <div class="d-flex gap-3 mb-4 pb-4" style="border-bottom: 1px solid var(--st-border);">
        <span class="icon-box icon-box--info icon-box--round flex-shrink-0"><i data-lucide="message-square"></i></span>
        <div>
          <div class="fw-medium">Amelia replied</div>
          <p class="text-muted-foreground m-0 mb-1 fs-2">"Let's pair on the cart bug tomorrow morning."</p>
          <span class="fs-2 text-muted-foreground">12 minutes ago</span>
        </div>
      </div>
      <div class="d-flex gap-3">
        <span class="icon-box icon-box--warning icon-box--round flex-shrink-0"><i data-lucide="alert-triangle"></i></span>
        <div>
          <div class="fw-medium">Disk above 80%</div>
          <p class="text-muted-foreground m-0 mb-1 fs-2">db-primary-2 is at 84% used. Consider expanding the volume.</p>
          <span class="fs-2 text-muted-foreground">35 minutes ago</span>
        </div>
      </div>
    </div>
  </div>
</div>

<div class="drawer drawer--top" data-stisla-drawer id="drawerTop" style="--drawer-height: 16rem;">
  <div class="drawer__backdrop" data-stisla-drawer-dismiss></div>
  <div class="drawer__content">
    <div class="drawer__header">
      <h3 class="drawer__title">What's new in June</h3>
      <button type="button" class="drawer__close" data-stisla-drawer-dismiss aria-label="Close"><i data-lucide="x"></i></button>
    </div>
    <div class="drawer__body">
      <div class="row g-3">
        <div class="col-md-4">
          <span class="badge badge--primary mb-2">New</span>
          <div class="fw-medium mb-1">Bulk reassign</div>
          <p class="text-muted-foreground m-0 fs-2">Move many tasks to a new owner in one go from the board view.</p>
        </div>
        <div class="col-md-4">
          <span class="badge badge--success mb-2">Improved</span>
          <div class="fw-medium mb-1">Faster search</div>
          <p class="text-muted-foreground m-0 fs-2">Workspace search now returns results in under 100ms for most queries.</p>
        </div>
        <div class="col-md-4">
          <span class="badge badge--warning mb-2">Fixed</span>
          <div class="fw-medium mb-1">Recurring task DST</div>
          <p class="text-muted-foreground m-0 fs-2">Recurring tasks no longer drift by an hour around daylight saving.</p>
        </div>
      </div>
    </div>
  </div>
</div>

<div class="drawer drawer--bottom" data-stisla-drawer id="drawerBottom" style="--drawer-height: 14rem;">
  <div class="drawer__backdrop" data-stisla-drawer-dismiss></div>
  <div class="drawer__content">
    <div class="drawer__header">
      <div class="mx-auto w-100 d-flex align-items-center" style="max-width: 30rem;">
        <h3 class="drawer__title">Share this report</h3>
        <button type="button" class="drawer__close" data-stisla-drawer-dismiss aria-label="Close"><i data-lucide="x"></i></button>
      </div>
    </div>
    <div class="drawer__body">
      <div class="mx-auto w-100" style="max-width: 30rem;">
        <div class="input-group mb-4">
          <input type="text" class="input" value="https://app.example.com/r/q3-revenue" readonly>
          <button class="btn btn--outline btn--neutral" type="button">Copy link</button>
        </div>
        <div class="d-flex flex-wrap gap-2">
          <button type="button" class="btn btn--outline btn--neutral"><i data-lucide="mail"></i>Email</button>
          <button type="button" class="btn btn--outline btn--neutral"><i data-lucide="message-circle"></i>Slack</button>
          <button type="button" class="btn btn--outline btn--neutral"><i data-lucide="download"></i>Download PDF</button>
          <button type="button" class="btn btn--outline btn--neutral"><i data-lucide="printer"></i>Print</button>
        </div>
      </div>
    </div>
  </div>
</div>

Sized

Override --drawer-width (start/end) or --drawer-height (top/bottom) inline on the root to retune a single instance. The default width is 22rem; this one widens to 28rem for a roomier form layout.

Edit profile

<button type="button" class="btn btn--primary" data-stisla-drawer-trigger="drawerSized">
  Edit profile
</button>

<div class="drawer" data-stisla-drawer id="drawerSized" style="--drawer-width: 28rem;">
  <div class="drawer__backdrop" data-stisla-drawer-dismiss></div>
  <div class="drawer__content">
    <div class="drawer__header">
      <h3 class="drawer__title">Edit profile</h3>
      <button type="button" class="drawer__close" data-stisla-drawer-dismiss aria-label="Close"><i data-lucide="x"></i></button>
    </div>
    <div class="drawer__body">
      <div class="row g-3">
        <div class="col-6">
          <label for="profileFirst" class="form-label">First name</label>
          <input type="text" class="input" id="profileFirst" value="Nauval">
        </div>
        <div class="col-6">
          <label for="profileLast" class="form-label">Last name</label>
          <input type="text" class="input" id="profileLast" value="Azhar">
        </div>
        <div class="col-12">
          <label for="profileBio" class="form-label">Bio</label>
          <textarea class="textarea" id="profileBio" rows="3">Designer and maintainer of Stisla.</textarea>
        </div>
        <div class="col-12">
          <label for="profileEmail" class="form-label">Email</label>
          <input type="email" class="input" id="profileEmail" value="nauvalazhar2@gmail.com">
        </div>
      </div>
    </div>
    <div class="drawer__footer">
      <button type="button" class="btn btn--ghost btn--neutral" data-stisla-drawer-dismiss>Cancel</button>
      <button type="button" class="btn btn--primary" data-stisla-drawer-dismiss>Save changes</button>
    </div>
  </div>
</div>

Body scroll allowed

Set data-stisla-drawer-scroll="true" to let the page behind keep scrolling while the panel is open. Useful for activity feeds or reference panels that support the main task without interrupting it.

Activity

  1. Amelia pushed 3 commits to main
    9:42 AM
  2. Jonas completed "Audit checkout copy"
    9:31 AM
  3. Priya commented on "Onboarding redesign"

    "Step 3 looks good. The empty state on step 4 still needs a copy pass."

    9:18 AM
  4. Lena joined the workspace
    Yesterday
  5. Build #2143 failed on release/v2
    Yesterday
<button type="button" class="btn btn--primary" data-stisla-drawer-trigger="drawerActivity">
  Open activity
</button>

<div class="drawer" data-stisla-drawer data-stisla-drawer-scroll="true" data-stisla-drawer-backdrop="false" id="drawerActivity">
  <div class="drawer__backdrop" data-stisla-drawer-dismiss></div>
  <div class="drawer__content">
    <div class="drawer__header">
      <h3 class="drawer__title">Activity</h3>
      <button type="button" class="drawer__close" data-stisla-drawer-dismiss aria-label="Close"><i data-lucide="x"></i></button>
    </div>
    <div class="drawer__body">
      <ol class="p-0 m-0" style="list-style: none;">
        <li class="d-flex gap-3 mb-4">
          <div class="icon-box icon-box--primary icon-box--round flex-shrink-0" style="--icon-box-size: 2rem;"><i data-lucide="git-commit"></i></div>
          <div>
            <div><span class="fw-medium">Amelia</span> pushed 3 commits to <code>main</code></div>
            <span class="fs-2 text-muted-foreground">9:42 AM</span>
          </div>
        </li>
        <li class="d-flex gap-3 mb-4">
          <div class="icon-box icon-box--success icon-box--round flex-shrink-0" style="--icon-box-size: 2rem;"><i data-lucide="check"></i></div>
          <div>
            <div><span class="fw-medium">Jonas</span> completed "Audit checkout copy"</div>
            <span class="fs-2 text-muted-foreground">9:31 AM</span>
          </div>
        </li>
        <li class="d-flex gap-3 mb-4">
          <div class="icon-box icon-box--info icon-box--round flex-shrink-0" style="--icon-box-size: 2rem;"><i data-lucide="message-square"></i></div>
          <div>
            <div><span class="fw-medium">Priya</span> commented on "Onboarding redesign"</div>
            <p class="text-muted-foreground m-0 mb-1 fs-2">"Step 3 looks good. The empty state on step 4 still needs a copy pass."</p>
            <span class="fs-2 text-muted-foreground">9:18 AM</span>
          </div>
        </li>
        <li class="d-flex gap-3 mb-4">
          <div class="icon-box icon-box--warning icon-box--round flex-shrink-0" style="--icon-box-size: 2rem;"><i data-lucide="user-plus"></i></div>
          <div>
            <div><span class="fw-medium">Lena</span> joined the workspace</div>
            <span class="fs-2 text-muted-foreground">Yesterday</span>
          </div>
        </li>
        <li class="d-flex gap-3">
          <div class="icon-box icon-box--danger icon-box--round flex-shrink-0" style="--icon-box-size: 2rem;"><i data-lucide="alert-circle"></i></div>
          <div>
            <div>Build #2143 failed on <code>release/v2</code></div>
            <span class="fs-2 text-muted-foreground">Yesterday</span>
          </div>
        </li>
      </ol>
    </div>
  </div>
</div>

Static backdrop

Set data-stisla-drawer-backdrop="static" and data-stisla-drawer-keyboard="false" to force a deliberate dismiss. The backdrop click shakes the panel along its slide axis instead of closing. Explicit dismiss controls still close.

Finish your profile

A few details so teammates know who they're working with.

<button type="button" class="btn btn--primary" data-stisla-drawer-trigger="drawerSetup">
  Finish setup
</button>

<div class="drawer" data-stisla-drawer data-stisla-drawer-backdrop="static" data-stisla-drawer-keyboard="false" id="drawerSetup">
  <div class="drawer__backdrop" data-stisla-drawer-dismiss></div>
  <div class="drawer__content">
    <div class="drawer__header">
      <h3 class="drawer__title">Finish your profile</h3>
    </div>
    <div class="drawer__body">
      <p class="text-muted-foreground">A few details so teammates know who they're working with.</p>
      <div class="mb-4">
        <label for="setupName" class="form-label">Full name</label>
        <input type="text" class="input" id="setupName" value="Nauval Azhar">
      </div>
      <div class="mb-4">
        <label for="setupRole" class="form-label">Role</label>
        <select class="select" id="setupRole">
          <option>Engineering</option>
          <option selected>Design</option>
          <option>Product</option>
          <option>Operations</option>
        </select>
      </div>
      <div>
        <label for="setupTimezone" class="form-label">Timezone</label>
        <select class="select" id="setupTimezone">
          <option>UTC−05:00 New York</option>
          <option>UTC+00:00 London</option>
          <option selected>UTC+07:00 Jakarta</option>
          <option>UTC+09:00 Tokyo</option>
        </select>
      </div>
    </div>
    <div class="drawer__footer">
      <button type="button" class="btn btn--primary w-100" data-stisla-drawer-dismiss>Save and continue</button>
    </div>
  </div>
</div>

No backdrop

Set data-stisla-drawer-backdrop="false" to drop the dim entirely so the panel sits alongside the main content. Pair with data-stisla-drawer-scroll="true" for filter panels and inspector strips the user wants to keep open while they read or click around.

Filters

Status
Priority
<button type="button" class="btn btn--outline btn--neutral" data-stisla-drawer-trigger="drawerFilters">
  <i data-lucide="filter"></i>Filters
</button>

<div class="drawer drawer--start" data-stisla-drawer data-stisla-drawer-backdrop="false" data-stisla-drawer-scroll="true" id="drawerFilters">
  <div class="drawer__backdrop" data-stisla-drawer-dismiss></div>
  <div class="drawer__content">
    <div class="drawer__header">
      <h3 class="drawer__title">Filters</h3>
      <button type="button" class="drawer__close" data-stisla-drawer-dismiss aria-label="Close"><i data-lucide="x"></i></button>
    </div>
    <div class="drawer__body">
      <div class="mb-6">
        <div class="fw-medium mb-2">Status</div>
        <div class="field-row">
          <input class="checkbox" type="checkbox" id="fStatusOpen" checked>
          <label class="field-row__label" for="fStatusOpen">Open</label>
        </div>
        <div class="field-row">
          <input class="checkbox" type="checkbox" id="fStatusProgress" checked>
          <label class="field-row__label" for="fStatusProgress">In progress</label>
        </div>
        <div class="field-row">
          <input class="checkbox" type="checkbox" id="fStatusReview">
          <label class="field-row__label" for="fStatusReview">In review</label>
        </div>
        <div class="field-row">
          <input class="checkbox" type="checkbox" id="fStatusDone">
          <label class="field-row__label" for="fStatusDone">Done</label>
        </div>
      </div>
      <div class="mb-6">
        <div class="fw-medium mb-2">Priority</div>
        <div class="field-row">
          <input class="checkbox" type="checkbox" id="fPriorityHigh" checked>
          <label class="field-row__label" for="fPriorityHigh">High</label>
        </div>
        <div class="field-row">
          <input class="checkbox" type="checkbox" id="fPriorityMed">
          <label class="field-row__label" for="fPriorityMed">Medium</label>
        </div>
        <div class="field-row">
          <input class="checkbox" type="checkbox" id="fPriorityLow">
          <label class="field-row__label" for="fPriorityLow">Low</label>
        </div>
      </div>
      <div>
        <label for="fAssignee" class="form-label fw-medium">Assignee</label>
        <select class="select" id="fAssignee">
          <option selected>Anyone</option>
          <option>Me</option>
          <option>Amelia</option>
          <option>Jonas</option>
          <option>Priya</option>
        </select>
      </div>
    </div>
    <div class="drawer__footer">
      <button type="button" class="btn btn--ghost btn--neutral" data-stisla-drawer-dismiss>Reset</button>
      <button type="button" class="btn btn--primary" data-stisla-drawer-dismiss>Apply filters</button>
    </div>
  </div>
</div>

Customization

Override these on the .drawer root (or globally) to retune a single instance.

Geometry

VariableDefaultUse
--drawer-width22remPanel width for --start and --end (default).
--drawer-height16remPanel height for --top and --bottom.
--drawer-paddingcalc(1.25rem * var(--st-density))Header and body padding. Scales with --st-density.
--drawer-z-index1045Stack level. One tier below dialog.

Surface

VariableDefaultUse
--drawer-bgvar(--st-surface)Panel fill.
--drawer-colorvar(--st-foreground)Panel text color.
--drawer-border-colorvar(--st-border)Inner-edge border (only the side facing the viewport).
--drawer-shadow0 0 40px -10px color-mix(…)Soft ambient shadow around the panel.

Backdrop

VariableDefaultUse
--drawer-backdrop-bgoklch(0 0 0 / 0.55)Dim color over the page behind.
--drawer-backdrop-blur12pxBackdrop blur radius.

Title

VariableDefaultUse
--drawer-title-font-size1.125remPins the title size so any heading tag reads the same.
--drawer-title-font-weight600Title weight.

Close chip

VariableDefaultUse
--drawer-close-size1.75remWidth and height of the dismiss chip.
--drawer-close-colorvar(--st-muted-foreground)Resting icon color.
--drawer-close-color-hovervar(--st-foreground)Hover and focus icon color.
--drawer-close-bg-hovervar(--st-accent)Hover and focus chip background.
VariableDefaultUse
--drawer-footer-padding-ycalc(0.875rem * var(--st-density))Vertical padding for the footer band.
--drawer-footer-bgvar(--st-surface-2)Footer fill. Tints to the alt surface so the seam reads against the body.
--drawer-footer-border-colorvar(--st-border)Top border of the footer band.

Motion

VariableDefaultUse
--drawer-transition-duration0.25sSlide and backdrop fade duration. Zeroed under prefers-reduced-motion: reduce.